Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android系統教程 >> Android開發教程 >> Android滑動菜單框架完全解析,教你如何一分鐘實現滑動菜單特效

Android滑動菜單框架完全解析,教你如何一分鐘實現滑動菜單特效

編輯:Android開發教程

之前我向大家介紹了史上最簡單的滑動菜單的實現方式,相信大家都還記得。如果忘記了其中的實現原理 或者還沒看過的朋友,請先去看一遍之前的文章 Android滑動菜單特效實現,仿人人客戶端側滑效果,史上 最簡單的側滑實現 ,因為我們今天要實現的滑動菜單框架也是基於同樣的原理的。

之前的文章中在 最後也提到了,如果是你的應用程序中有很多個Activity都需要加入滑動菜單的功能,那麼每個Activity都 要寫上百行的代碼才能實現效果,再簡單的滑動菜單實現方案也沒用。因此我們今天要實現一個滑動菜單的 框架,然後在任何Activity中都可以一分鐘引入滑動菜單功能。

首先還是講一下實現原理。說是滑動 菜單的框架,其實說白了也很簡單,就是我們自定義一個布局,在這個自定義布局中實現好滑動菜單的功能 ,然後只要在Activity的布局文件裡面引入我們自定義的布局,這個Activity就擁有了滑動菜單的功能了。 原理講完了,是不是很簡單?下面我們來動手實現吧。

在Eclipse中新建一個Android項目,項目名就 叫做RenRenSlidingLayout。

新建一個類,名叫SlidingLayout,這個類是繼承自LinearLayout的,並 且實現了OnTouchListener接口,具體代碼如下:

public class SlidingLayout extends LinearLayout implements OnTouchListener {  
      
    /** 
     * 滾動顯示和隱藏左側布局時,手指滑動需要達到的速度。 
     */
    public static final int SNAP_VELOCITY = 200;  
      
    /** 
     * 屏幕寬度值。 
     */
    private int screenWidth;  
      
    /** 
     * 左側布局最多可以滑動到的左邊緣。值由左側布局的寬度來定,marginLeft到達此值之後,不能再減

少。 
     */
    private int leftEdge;  
      
    /** 
     * 左側布局最多可以滑動到的右邊緣。值恆為0,即marginLeft到達0之後,不能增加。 
     */
    private int rightEdge = 0;  
      
    /** 
     * 左側布局完全顯示時,留給右側布局的寬度值。 
     */
    private int leftLayoutPadding = 80;  
      
    /** 
     * 記錄手指按下時的橫坐標。 
     */
    private float xDown;  
      
    /** 
     * 記錄手指移動時的橫坐標。 
     */
    private float xMove;  
      
    /** 
     * 記錄手機抬起時的橫坐標。 
     */
    private float xUp;  
      
    /** 
     * 左側布局當前是顯示還是隱藏。只有完全顯示或隱藏時才會更改此值,滑動過程中此值無效。 
     */
    private boolean isLeftLayoutVisible;  
      
    /** 
     * 左側布局對象。 
     */
    private View leftLayout;  
      
    /** 
     * 右側布局對象。 
     */
    private View rightLayout;  
      
    /** 
     * 用於監聽側滑事件的View。 
     */
    private View mBindView;  
      
    /** 
     * 左側布局的參數,通過此參數來重新確定左側布局的寬度,以及更改leftMargin的值。 
     */
    private MarginLayoutParams leftLayoutParams;  
      
    /** 
     * 右側布局的參數,通過此參數來重新確定右側布局的寬度。 
     */
    private MarginLayoutParams rightLayoutParams;  
      
    /** 
     * 用於計算手指滑動的速度。 
     */
    private VelocityTracker mVelocityTracker;  
      
    /** 
     * 重寫SlidingLayout的構造函數,其中獲取了屏幕的寬度。 
     *  
     * @param context 
     * @param attrs 
     */
    public SlidingLayout(Context context, AttributeSet attrs) {  
        super(context, attrs);  
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);  
        screenWidth = wm.getDefaultDisplay().getWidth();  
    }  
      
    /** 
     * 綁定監聽側滑事件的View,即在綁定的View進行滑動才可以顯示和隱藏左側布局。 
     *  
     * @param bindView 
     *            需要綁定的View對象。 
     */
    public void setScrollEvent(View bindView) {  
        mBindView = bindView;  
        mBindView.setOnTouchListener(this);  
    }  
      
    /** 
     * 將屏幕滾動到左側布局界面,滾動速度設定為30. 
     */
    public void scrollToLeftLayout() {  
        new ScrollTask().execute(30);  
    }  
      
    /** 
     * 將屏幕滾動到右側布局界面,滾動速度設定為-30. 
     */
    public void scrollToRightLayout() {  
        new ScrollTask().execute(-30);  
    }  
      
    /** 
     * 左側布局是否完全顯示出來,或完全隱藏,滑動過程中此值無效。 
     *  
     * @return 左側布局完全顯示返回true,完全隱藏返回false。 
     */
    public boolean isLeftLayoutVisible() {  
        return isLeftLayoutVisible;  
    }  
      
    /** 
     * 在onLayout中重新設定左側布局和右側布局的參數。 
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {  
        super.onLayout(changed, l, t, r, b);  
        if (changed) {  
            // 獲取左側布局對象  
            leftLayout = getChildAt(0);  
            leftLayoutParams = (MarginLayoutParams) leftLayout.getLayoutParams();  
            // 重置左側布局對象的寬度為屏幕寬度減去leftLayoutPadding  
            leftLayoutParams.width = screenWidth - leftLayoutPadding;  
            // 設置最左邊距為負的左側布局的寬度  
            leftEdge = -leftLayoutParams.width;  
            leftLayoutParams.leftMargin = leftEdge;  
            leftLayout.setLayoutParams(leftLayoutParams);  
            // 獲取右側布局對象  
            rightLayout = getChildAt(1);  
            rightLayoutParams = (MarginLayoutParams) rightLayout.getLayoutParams();  
            rightLayoutParams.width = screenWidth;  
            rightLayout.setLayoutParams(rightLayoutParams);  
        }  
    }  
      
    @Override
    public boolean onTouch(View v, MotionEvent event) {  
        createVelocityTracker(event);  
        switch (event.getAction()) {  
        case MotionEvent.ACTION_DOWN:  
            // 手指按下時,記錄按下時的橫坐標  
            xDown = event.getRawX();  
            break;  
        case MotionEvent.ACTION_MOVE:  
            // 手指移動時,對比按下時的橫坐標,計算出移動的距離,來調整左側布局的leftMargin值,從而顯示和隱藏左側布局  
            xMove = event.getRawX();  
            int distanceX = (int) (xMove - xDown);  
            if (isLeftLayoutVisible) {  
                leftLayoutParams.leftMargin = distanceX;  
            } else {  
                leftLayoutParams.leftMargin = leftEdge + distanceX;  
            }  
            if (leftLayoutParams.leftMargin < leftEdge) {  
                leftLayoutParams.leftMargin = leftEdge;  
            } else if (leftLayoutParams.leftMargin > rightEdge) {  
                leftLayoutParams.leftMargin = rightEdge;  
            }  
            leftLayout.setLayoutParams(leftLayoutParams);  
            break;  
        case MotionEvent.ACTION_UP:  
            // 手指抬起時,進行判斷當前手勢的意圖,從而決定是滾動到左側布局,還是滾動到右側布局 

 
            xUp = event.getRawX();  
            if (wantToShowLeftLayout()) {  
                if (shouldScrollToLeftLayout()) {  
                    scrollToLeftLayout();  
                } else {  
                    scrollToRightLayout();  
                }  
            } else if (wantToShowRightLayout()) {  
                if (shouldScrollToContent()) {  
                    scrollToRightLayout();  
                } else {  
                    scrollToLeftLayout();  
                }  
            }  
            recycleVelocityTracker();  
            break;  
        }  
        return isBindBasicLayout();  
    }  
      
    /** 
     * 判斷當前手勢的意圖是不是想顯示右側布局。如果手指移動的距離是負數,且當前左側布局是可見的,則認為當前手勢是想要顯示右側布局。 
     *  
     * @return 當前手勢想顯示右側布局返回true,否則返回false。 
     */
    private boolean wantToShowRightLayout() {  
        return xUp - xDown < 0 && isLeftLayoutVisible;  
    }  
      
    /** 
     * 判斷當前手勢的意圖是不是想顯示左側布局。如果手指移動的距離是正數,且當前左側布局是不可見的,則認為當前手勢是想要顯示左側布局。 
     *  
     * @return 當前手勢想顯示左側布局返回true,否則返回false。 
     */
    private boolean wantToShowLeftLayout() {  
        return xUp - xDown > 0 && !isLeftLayoutVisible;  
    }  
      
    /** 
     * 判斷是否應該滾動將左側布局展示出來。如果手指移動距離大於屏幕的1/2,或者手指移動速度大於SNAP_VELOCITY, 
     * 就認為應該滾動將左側布局展示出來。 
     *  
     * @return 如果應該滾動將左側布局展示出來返回true,否則返回false。 
     */
    private boolean shouldScrollToLeftLayout() {  
        return xUp - xDown > screenWidth / 2 || getScrollVelocity() > SNAP_VELOCITY;  
    }  
      
    /** 
     * 判斷是否應該滾動將右側布局展示出來。如果手指移動距離加上leftLayoutPadding大於屏幕的1/2, 
     * 或者手指移動速度大於SNAP_VELOCITY, 就認為應該滾動將右側布局展示出來。 
     *  
     * @return 如果應該滾動將右側布局展示出來返回true,否則返回false。 
     */
    private boolean shouldScrollToContent() {  
        return xDown - xUp + leftLayoutPadding > screenWidth / 2
                || getScrollVelocity() > SNAP_VELOCITY;  
    }  
      
    /** 
     * 判斷綁定滑動事件的View是不是一個基礎layout,不支持自定義layout,只支持四種基本layout, 
     * AbsoluteLayout已被棄用。 
     *  
     * @return 如果綁定滑動事件的View是LinearLayout,RelativeLayout,FrameLayout, 
     *         TableLayout之一就返回true,否則返回false。 
     */
    private boolean isBindBasicLayout() {  
        if (mBindView == null) {  
            return false;  
        }  
        String viewName = mBindView.getClass().getName();  
        return viewName.equals(LinearLayout.class.getName())  
                || viewName.equals(RelativeLayout.class.getName())  
                || viewName.equals(FrameLayout.class.getName())  
                || viewName.equals(TableLayout.class.getName());  

    }  
      
    /** 
     * 創建VelocityTracker對象,並將觸摸事件加入到VelocityTracker當中。 
     *  
     * @param event 
     *            右側布局監聽控件的滑動事件 
     */
    private void createVelocityTracker(MotionEvent event) {  
        if (mVelocityTracker == null) {  
            mVelocityTracker = VelocityTracker.obtain();  
        }  
        mVelocityTracker.addMovement(event);  
    }  
      
    /** 
     * 獲取手指在右側布局的監聽View上的滑動速度。 
     *  
     * @return 滑動速度,以每秒鐘移動了多少像素值為單位。 
     */
    private int getScrollVelocity() {  
        mVelocityTracker.computeCurrentVelocity(1000);  
        int velocity = (int) mVelocityTracker.getXVelocity();  
        return Math.abs(velocity);  
    }  
      
    /** 
     * 回收VelocityTracker對象。 
     */
    private void recycleVelocityTracker() {  
        mVelocityTracker.recycle();  
        mVelocityTracker = null;  
    }  
      
    class ScrollTask extends AsyncTask<Integer, Integer, Integer> {  
      
        @Override
        protected Integer doInBackground(Integer... speed) {  
            int leftMargin = leftLayoutParams.leftMargin;  
            // 根據傳入的速度來滾動界面,當滾動到達左邊界或右邊界時,跳出循環。  
            while (true) {  
                leftMargin = leftMargin + speed[0];  
                if (leftMargin > rightEdge) {  
                    leftMargin = rightEdge;  
                    break;  
                }  
                if (leftMargin < leftEdge) {  
                    leftMargin = leftEdge;  
                    break;  
                }  
                publishProgress(leftMargin);  
                // 為了要有滾動效果產生,每次循環使線程睡眠20毫秒,這樣肉眼才能夠看到滾動動畫。 

 
                sleep(20);  
            }  
            if (speed[0] > 0) {  
                isLeftLayoutVisible = true;  
            } else {  
                isLeftLayoutVisible = false;  
            }  
            return leftMargin;  
        }  
      
        @Override
        protected void onProgressUpdate(Integer... leftMargin) {  
            leftLayoutParams.leftMargin = leftMargin[0];  
            leftLayout.setLayoutParams(leftLayoutParams);  
        }  
      
        @Override
        protected void onPostExecute(Integer leftMargin) {  
            leftLayoutParams.leftMargin = leftMargin;  
            leftLayout.setLayoutParams(leftLayoutParams);  
        }  
    }  
      
    /** 
     * 使當前線程睡眠指定的毫秒數。 
     *  
     * @param millis 
     *            指定當前線程睡眠多久,以毫秒為單位 
     */
    private void sleep(long millis) {  
        try {  
            Thread.sleep(millis);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
    }  
}

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