Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android側滑菜單完整詳細示例(基礎版)

Android側滑菜單完整詳細示例(基礎版)

編輯:關於Android編程

MainActivity如下:
package cc.cd;

import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.app.Activity;
import android.content.Context;
/**
 * Demo描述:
 * 側滑菜單SlidingMenu的簡單示例
 * 即在一個Activity中加入SlidingMenu
 * 
 * 示例說明:
 * 示例中一共兩個界面menu和content.當進入應用時顯示content界面.
 * 若手指滑向屏幕右側則顯示menu和隱藏content.反之,同理.
 * menu的顯示和隱藏主要是修改menu的LayoutParams中的leftMargin從而達到效果.
 * 
 * 步驟整理:
 * 1 初始化時,通過設置menu的LayoutParams中的leftMargin使其完全隱藏.
 * 2 為content設置Touch監聽.
 * 3 在move的過程中不斷修改menu的LayoutParams中的leftMargin使其按照
 *   手勢的滑動而顯示或者隱藏
 * 4 在up時通過異步任務AsyncTask修改menu的LayoutParams中的leftMargin
 *   從而實現menu的完全顯示或者完全隱藏.
 *   
 * 以上套路還是挺常見的.
 * 
 * 參考資料:
 * 1 http://blog.csdn.net/guolin_blog/article/details/8714621
 * 2 http://blog.csdn.net/hudashi/article/details/7352157
 *   Thank you very much
 * 
 * 備注說明:
 * 1 示例中使用的圖片亦來自參考資料1
 * 2 為簡化邏輯,示例中的兩個界面均為截圖而不是實際View界面
 */
public class MainActivity extends Activity {
	private int screenWidth;
    private View contentView;
    private View menuView;
    private float xDown;
    private float xMove;
    private float xUp;
    //當完全顯示menu時content的寬度最小值
    private int contentViewMinWidth = 80;
    //menu是否可見的標志位,該值在滑動過程中無效.
    //只有在滑動結束後,完全顯示或隱藏menu時才會更改此值
    private boolean isMenuVisible=false;
    private int menuParamsMaxLeftMargin=0;
    private int menuParamsMinLeftMargin=0;
    //速度追蹤
    private VelocityTracker mVelocityTracker;
    //阈值
    public static final int VELOCITY_THRESHOLD=200;
    //TAG
    private final static String TAG="MainActivity";
    //menu的布局LayoutParams
    private LinearLayout.LayoutParams menuLayoutParams;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		init();
	}

	private void init(){
		//獲取屏幕寬度
		WindowManager windowManager=(WindowManager) getSystemService(Context.WINDOW_SERVICE);
		screenWidth=windowManager.getDefaultDisplay().getWidth();
		
		//初始化contentView
		contentView=findViewById(R.id.contentLinearLayout);
		//將contentView的寬度設置為屏幕的寬度
		contentView.getLayoutParams().width=screenWidth;
		//設置Touch監聽
		contentView.setOnTouchListener(new TouchListenerImpl());
		
		//初始化menuView
		menuView=findViewById(R.id.menuLinearLayout);
		menuLayoutParams=(LinearLayout.LayoutParams) menuView.getLayoutParams();
		//設置menuView的寬度.
		//設置其寬度為屏幕寬度減去contentView的最小寬度
		menuLayoutParams.width=screenWidth-contentViewMinWidth;
		//初始化時完全隱藏了menuView.
		menuParamsMinLeftMargin=-menuLayoutParams.width;
		menuLayoutParams.leftMargin=menuParamsMinLeftMargin;
	}
	
	/**
	 * 關於ACTION_MOVE中distanceX的細節說明.
	 * 在一次滑動過程中(從手指按下到手指抬起)distanceX的值是持續變大或者變小的.
	 * 因為int distanceX=(int) (xMove-xDown);
	 * 這個xDown是按下時的坐標值,在Moving的過程中計算distanceX時一直采用
	 * 的是xDown減去每時刻的xMove.即在滑動過程中xDown一直不變而xMove是不斷
	 * 在變化的.
	 * 代碼說明:
	 * if (isMenuVisible) {
	 *     menuLayoutParams.leftMargin=distanceX;
	 * } else {
	 *    menuLayoutParams.leftMargin=menuParamsMinLeftMargin+distanceX;
	 * }
	 * 在最開始時,menu是隱藏的.手指按下,滑向屏幕的右邊.調用:
	 * menuLayoutParams.leftMargin=menuParamsMinLeftMargin+distanceX;
	 * 所以這個menuLayoutParams.leftMargin是不斷在變大的直到0為止(注意越界判斷),此時
	 * menuView完全顯示.這時手指再按下,滑向屏幕的左邊.調用:
	 * menuLayoutParams.leftMargin=distanceX;
	 * distanceX這個負數一直在減小,它就是menuLayoutParams.leftMargin的值直至
	 * menuView完全隱藏為止,此時它的值為menuParamsMinLeftMargin(注意越界判斷).
	 * 
	 * 該問題不難,但在此備注一下以防以後反應不過來.
	 */
	private class TouchListenerImpl implements OnTouchListener{
		@Override
		public boolean onTouch(View v, MotionEvent event) {
			//開始速度追蹤
			startVelocityTracker(event);
			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:
				xDown=event.getRawX();
				break;
			case MotionEvent.ACTION_MOVE:
				xMove=event.getRawX();
				int distanceX=(int) (xMove-xDown);
				Log.i(TAG, "xDown="+xDown+",xMove="+xMove+",distanceX="+distanceX);
				if (isMenuVisible) {
					menuLayoutParams.leftMargin=distanceX;
				} else {
					menuLayoutParams.leftMargin=menuParamsMinLeftMargin+distanceX;
				}
				
				//處理越界的情況
				if(menuLayoutParams.leftMarginmenuParamsMaxLeftMargin){
					menuLayoutParams.leftMargin=menuParamsMaxLeftMargin;
				}
				//設置menuView的LayoutParams
				menuView.setLayoutParams(menuLayoutParams);
				break;
			case MotionEvent.ACTION_UP:
				xUp=event.getRawX();
				
				//判斷手勢意圖想顯示menu
				if (wantToShowMenu()) {
					//判斷是否顯示menu
					if (shouldScrollToMenu()) {
						scrollToMenu();
					} else {
						scrollToContent();
					}
				}
				
				//判斷手勢意圖想顯示content
				if (wantToShowContent()) {
					//判斷是否顯示content
					if (shouldScrollToContent()) {
						scrollToContent();
					} else {
						scrollToMenu();
					}
				}
				//終止速度追蹤
				stopVelocityTracker();
				break;
			default:
				break;
			}
			return true;
		}
		
	}
	
	/**
	 * 判斷當前手勢是否想顯示菜單Menu
	 * 判斷條件:
	 * 1 抬起坐標大於按下坐標
	 * 2 menu本身不可見
	 */
	private boolean wantToShowMenu(){
		return ((xUp-xDown>0)&&(!isMenuVisible));
	}
	
	/**
	 * 判斷是否應該將menu完整顯示出來
	 * 判斷條件:
	 * 滑動距離大於屏幕的二分之一
	 * 或者滑動速度大於速度阈值VELOCITY_THRESHOLD
	 */
	private boolean shouldScrollToMenu(){
		return ((xUp-xDown>screenWidth/2)||(getScrollVelocity()>VELOCITY_THRESHOLD));
	}
	
	/**
	 * 將屏幕滾動到menu.即將menu完整顯示.
	 * 按照30的步調不斷修改修改menu的LayoutParams中的leftMargin
	 */
	private void scrollToMenu(){
		new ScrollAsyncTask().execute(30);
	}
	
	/**
	 * 判斷當前手勢是否想顯示菜單Content
	 * 判斷條件:
	 * 1 抬起坐標小於按下坐標
	 * 2 menu本身可見
	 */
	private boolean wantToShowContent(){
		return ((xUp-xDown<0)&&(isMenuVisible));
	}
	
	
	/**
	 * 判斷是否應該將content完整顯示出來
	 * 判斷條件:
	 * xDown-xUp+contentViewMinWidth大於屏幕的二分之一
	 * 或者滑動速度大於速度阈值VELOCITY_THRESHOLD
	 */
	private boolean shouldScrollToContent(){
		return ((xDown-xUp+contentViewMinWidth>screenWidth/2)||(getScrollVelocity()>VELOCITY_THRESHOLD));
	}
	
	/**
	 * 將屏幕滾動到content.即將content完整顯示
	 * 按照-30的步調不斷修改修改menu的LayoutParams中的leftMargin
	 */
	private void scrollToContent(){
		new ScrollAsyncTask().execute(-30);
	}
	
	
	/**
	 * 開始速度追蹤
	 */
	private void startVelocityTracker(MotionEvent event){
		if (mVelocityTracker==null) {
			mVelocityTracker=VelocityTracker.obtain(); 
		}
		mVelocityTracker.addMovement(event);
	}
	
	/**
	 * 獲取在content上X方向的手指滑動速度
	 */
	private int getScrollVelocity(){
		//設置VelocityTracker單位.1000表示1秒時間內運動的像素 
		mVelocityTracker.computeCurrentVelocity(1000);
		//獲取在1秒內X方向所滑動像素值
		int xVelocity=(int) mVelocityTracker.getXVelocity();
		return Math.abs(xVelocity);
	}
	
	/**
	 * 終止速度追蹤
	 */
	private void stopVelocityTracker(){
		if (mVelocityTracker!=null) {
			mVelocityTracker.recycle();
			mVelocityTracker=null;
		}
	}
	
	
	/**
	 * 利用異步任務不斷修改menu的LayoutParams中的leftMargin從而達到一個
	 * 視圖移動的效果
	 */
	private class ScrollAsyncTask extends AsyncTask{
		@Override
		protected Integer doInBackground(Integer... speed) {
			int leftMargin=menuLayoutParams.leftMargin;
			while(true){
				//每次變化的speed
				leftMargin=leftMargin+speed[0];
				//若越界,則處理越界且跳出循環
				if (leftMargin>menuParamsMaxLeftMargin) {
					leftMargin=menuParamsMaxLeftMargin;
					break;
				}
				//若越界,則處理越界且跳出循環
				if (leftMargin0) {
				isMenuVisible=true;
			}else{
				isMenuVisible=false;
			}
			return leftMargin;
		}

		@Override
		protected void onProgressUpdate(Integer... leftMargin) {
			super.onProgressUpdate(leftMargin);
			menuLayoutParams.leftMargin=leftMargin[0];
			menuView.setLayoutParams(menuLayoutParams);
		}
		
		@Override
		protected void onPostExecute(Integer leftMargin) {
			super.onPostExecute(leftMargin);
			menuLayoutParams.leftMargin=leftMargin;
			menuView.setLayoutParams(menuLayoutParams);
		}
	}

}

main.xml如下:


    

    


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved