您好,登錄后才能下訂單哦!
package com.tenghu.sideslipmenu.view;
import android.content.Context;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout;
/**
* Created by Arvin_Li on 2014/11/18.
*/
public class SideslipMenuView extends LinearLayout {
//滾動顯示和隱藏menu時,手指滑動需要達到的速度
private static final int SNAP_VELOCITY = 200;
private View mMenu;//菜單布局
private View mContent;//主內容布局
private int mScreenWidth;//屏幕寬度
private int mMenuWidth;//菜單寬度
//menu最多可以滑動到的左邊緣,值由menu布局的寬度來定
private int leftEdge;
//menu最多可以滑到的有邊緣,值恒為0
private int rightEdge = 0;
//menu完全顯示時,留給content的寬度值
private int toRightPaddingWidth = 50;
//menu布局的參數,通過此參數來更改leftMargin的值
private LinearLayout.LayoutParams menuParams;
//記錄手指按下的橫坐標
private float xDown;
//記錄手指抬起的橫坐標
private float xUp;
//記錄手指移動的橫坐標
private float xMove;
//當前menu是顯示還是隱藏,只有menu完全顯示和隱藏才會改變該值,滑動時不會改變
private boolean isMenuVisible;
//用于計算手指滑動的速度
private VelocityTracker mVelocityTracker;
private boolean once;
public SideslipMenuView(Context context) {
super(context);
init(context);
}
public SideslipMenuView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
/**
* 初始化
*/
private void init(Context context) {
//獲取WindowManager對象
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics displayMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
mScreenWidth = displayMetrics.widthPixels;//獲取屏幕寬度
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (!once) {
mMenu = getChildAt(0);//獲取菜單布局
mContent = getChildAt(1);//獲取內容布局
menuParams = (LayoutParams) mMenu.getLayoutParams();//獲取菜單參數
mMenuWidth = menuParams.width = mScreenWidth - toRightPaddingWidth;//設置菜單寬度
//左邊緣的值賦值為menu寬度的負數
leftEdge = -menuParams.width;
//menu的leftMargin設置為左邊緣的值,默認菜單不可見
menuParams.leftMargin = leftEdge;
mContent.getLayoutParams().width = mScreenWidth;//設置內容寬度
once = true;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
createVelocityTracker(event);//創建VelocityTracker對象
switch (event.getAction()) {
//手指按下
case MotionEvent.ACTION_DOWN:
xDown = event.getRawX();//記錄橫坐標
break;
//手指移動
case MotionEvent.ACTION_MOVE:
//手指移動時,對比按下的橫坐標,計算出移動的距離來調整menu的leftMargin值,從而顯示和隱藏menu
xMove = event.getRawX();
//計算獲取到距離
int distanceX = (int) (xMove - xDown);
if (isMenuVisible) {
menuParams.leftMargin = distanceX;
} else {
menuParams.leftMargin = leftEdge + distanceX;
}
//如果菜單的左邊界小于了可以滑動的左邊界
if (menuParams.leftMargin <= leftEdge) {
//將可以滑動的左邊界賦值給菜單左邊界
menuParams.leftMargin = leftEdge;
} else if (menuParams.leftMargin >= rightEdge) {
menuParams.leftMargin = rightEdge;
}
mMenu.setLayoutParams(menuParams);//設置菜單參數
break;
//手指抬起
case MotionEvent.ACTION_UP:
//手指抬起時,進行判斷當前手勢的意圖,從而決定是滾動到menu界面,還是滾動到content界面
xUp = event.getRawX();
if (wantToShowMenu()) {
if (shouldScrollToMenu()) {
scrollToMenu();
} else {
scrollToContent();
}
} else if (wantToShowContent()) {
if (shouldScrollToContent()) {
scrollToContent();
} else {
scrollToMenu();
}
}
//手指抬起是回收VelocityTracker對象
recycleVelocityTracker();
break;
}
return true;
}
/**
* 創建VelocityTracker對象,并將觸摸content界面的滑動時間加入到VelocityTracker中
*
* @param event
*/
private void createVelocityTracker(MotionEvent event) {
if (null == mVelocityTracker) {
mVelocityTracker = VelocityTracker.obtain();//初始化VelocityTracker
}
mVelocityTracker.addMovement(event);//添加界面滑動事件
}
/**
* 判斷當前手勢的意圖是不是想顯示content,如果手指移動的距離是負數,且當前menu是可見的,則認為當前手勢是想要顯示content。
*
* @return
*/
private boolean wantToShowContent() {
return xUp - xDown < 0 && isMenuVisible;
}
/**
* 判斷當前手勢的意圖是不是想顯示menu,如果手指移動的距離是正數,且當前menu是不可見,則認為當前手勢是顯示menu
*
* @return
*/
private boolean wantToShowMenu() {
return xUp - xDown > 0 && !isMenuVisible;
}
/**
* 判斷是否應該滾動將menu展示出來,如果手指移動距離大于屏幕的1/2,或者手指移動速度大于SNAP_VELOCITY,就認為應該滾動將menu展示出來。
*
* @return 如果應該滾動將menu展示出來返回true,否則返回false。
*/
private boolean shouldScrollToMenu() {
return xUp - xDown > mMenuWidth / 2 || getScrollVelocity() > SNAP_VELOCITY;
}
/**
* 判斷是否應該滾動將content展示出來,如果手指移動距離加上menuPadding大于屏幕的1/2,或者手指移動速度大于SNAP_VELOCITY, 就認為應該滾動將content展示出來。
*
* @return 如果應該滾動將content展示出來返回true,否則返回false。
*/
private boolean shouldScrollToContent() {
//這里菜單狀態為顯示,如果要菜單隱藏,那么手勢是從右到左滑動,這里手指按下的橫坐標就會比抬起時的橫坐標小
return xDown - xUp + toRightPaddingWidth > mMenuWidth / 2 || getScrollVelocity() > SNAP_VELOCITY;
}
/**
* 獲取手指在content上滑動的速度
*
* @return 滑動速度,以每秒鐘移動了多少像素為單位
*/
private int getScrollVelocity() {
mVelocityTracker.computeCurrentVelocity(1000);
int velocity = (int) mVelocityTracker.getXVelocity();//獲取x方向的速度值
return Math.abs(velocity);
}
/**
* 回收VelocityTracker對象
*/
private void recycleVelocityTracker() {
if (null != mVelocityTracker) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
/**
* 將屏幕滾動到menu界面,設置滾動速度為30
*/
private void scrollToMenu() {
isMenuVisible = true;
new ScrollTask().execute(30);
}
/**
* 將屏幕滾動到content界面,設置滾動速度為-30
*/
private void scrollToContent() {
isMenuVisible = false;
new ScrollTask().execute(-30);
}
/**
* 創建滾動任務類
*/
class ScrollTask extends AsyncTask<Integer, Integer, Integer> {
@Override
protected Integer doInBackground(Integer... speed) {
int leftMargin = menuParams.leftMargin;
//根據傳入的速度來滾動界面,當滾動到達左邊界或右邊界時跳出循環
while (true) {
leftMargin = leftMargin + speed[0];
if (leftMargin > rightEdge) {
leftMargin = rightEdge;
break;
}
if (leftMargin < leftEdge) {
leftMargin = leftEdge;
break;
}
//更新任務進度,會把值傳入到onProgressUpdate()方法中進行UI的更新
publishProgress(leftMargin);
//為了要有滾動效果產生,每次循環使線程睡眠20毫秒
sleep(20);
}
return leftMargin;
}
/**
* 這里的Intege參數對應AsyncTask中的第二個參數
* 在doInBackground方法當中,,每次調用publishProgress方法都會觸發onProgressUpdate執行
* onProgressUpdate是在UI線程中執行,所有可以對UI空間進行操作
*
* @param leftMargin
*/
@Override
protected void onProgressUpdate(Integer... leftMargin) {
menuParams.leftMargin = leftMargin[0];
mMenu.setLayoutParams(menuParams);
}
/**
* 執行異步結束,接收doInBackground()方法的返回值,接收到的返回值對應AsyncTask<Integer, Integer, Integer> 第3個參數
*
* @param leftMargin
*/
@Override
protected void onPostExecute(Integer leftMargin) {
menuParams.leftMargin = leftMargin;
mMenu.setLayoutParams(menuParams);
}
}
/**
* 是當前線程睡眠指定的毫秒數
*
* @param millis
*/
private void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
以上就是整個自定義側滑菜單控件的代碼,在布局文件直接使用即可如下:
<?xml version="1.0" encoding="utf-8"?>
<com.tenghu.sideslipmenu.view.SideslipMenuView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_01">
<include layout="@layout/left_menu" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_02"
android:orientation="vertical">
</LinearLayout>
</com.tenghu.sideslipmenu.view.SideslipMenuView>
其中include引用的就是一個菜單布局文件,如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:orientation="vertical">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_marginBottom="10dp">
<ImageView
android:id="@+id/iv_img_01"
android:layout_width="50dp"
android:layout_height="match_parent"
android:src="@drawable/app_01" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/iv_img_01"
android:gravity="center_vertical"
android:text="第一個Item" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_marginBottom="10dp">
<ImageView
android:id="@+id/iv_img_02"
android:layout_width="50dp"
android:layout_height="match_parent"
android:src="@drawable/app_02" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/iv_img_02"
android:gravity="center_vertical"
android:text="第二個Item" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_marginBottom="10dp">
<ImageView
android:id="@+id/iv_img_03"
android:layout_width="50dp"
android:layout_height="match_parent"
android:src="@drawable/app_03" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/iv_img_03"
android:gravity="center_vertical"
android:text="第三個Item" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_marginBottom="10dp">
<ImageView
android:id="@+id/iv_img_04"
android:layout_width="50dp"
android:layout_height="match_parent"
android:src="@drawable/app_04" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/iv_img_04"
android:gravity="center_vertical"
android:text="第四個Item" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_marginBottom="10dp">
<ImageView
android:id="@+id/iv_img_05"
android:layout_width="50dp"
android:layout_height="match_parent"
android:src="@drawable/app_05" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/iv_img_05"
android:gravity="center_vertical"
android:text="第五個Item" />
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
其中的菜單可以使用ListView去布局,這里做測試就沒有去使用了
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。