自定義View:側滑選單實現

雨幕青山發表於2017-04-28

上篇部落格我們們用動畫實現了側滑選單,如果感興趣可以通過傳送門去看一下。今天我們們通過另一種方式實現側滑選單

一、佈局

總佈局

<?xml version="1.0" encoding="utf-8"?>
<com.yushan.slidemenu.SlideMenu xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/slideMenu"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include
        android:id="@+id/dl_secondary"
        layout="@layout/layout_secondary" />

    <include
        android:id="@+id/dl_main"
        layout="@layout/layout_main" />

</com.yushan.slidemenu.SlideMenu>

這裡的佈局和動畫實現的基本上沒有區別,但是由於動畫實現的SlidingMenu繼承的是FrameLayout,而這次的SlidingMenu繼承的是ViewGroup。所以樣式如下:

這裡寫圖片描述

二、SlidingMenu實現

  • 重寫構造方法
    public SlideMenu(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    public SlideMenu(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public SlideMenu(Context context) {
        super(context);
        init(context);
    }

    private void init(Context context) {
        scroller = new Scroller(context);
    }
  • 測量放置子控制元件
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        menuView = getChildAt(0);
        menuViewWidth = menuView.getLayoutParams().width;
        menuView.measure(menuViewWidth, heightMeasureSpec);

        mainView = getChildAt(1);
        mainView.measure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean bl, int l, int t, int r, int b) {
        menuView.layout(l - menuViewWidth, t, l, b);

        mainView.layout(l, t, r, b);
    }
  • 重寫onTouchEvent方法
  @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                break;
            case MotionEvent.ACTION_MOVE:

                distanceY = event.getX() - downX;
                if (distanceY > 10) {
                    hasMove = true;
                } else {
                    hasMove = false;
                }

                if (distanceY < 0) {
                    distanceY = 0;
                } else if (distanceY > menuViewWidth) {
                    distanceY = menuViewWidth;
                }

                scrollTo((int) distanceY);
                break;
            case MotionEvent.ACTION_UP:
                if (hasMove) {
                    if (distanceY > menuViewWidth / 2) {
                        oldDistanceY = menuViewWidth;
                        mState = DragState.Open;
                    } else {
                        oldDistanceY = 0;
                        mState = DragState.Close;
                    }

                    int startX = (int) distanceY;
                    int destX = (int) oldDistanceY;

                    animationScroll(startX, destX);
                } else {
                    if (mState != DragState.Close) {
                        showMenu();
                    }
                }
                hasMove = false;
                break;
        }

        return true;
    }
  • 點選按鈕顯示/隱藏側滑選單
    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.iv_menu:
                slideMenu.showMenu();
                break;
        }
    }

SlidingMenu中程式碼:

    /**
     * 方法功能:側拉選單的顯示與隱藏
     */
    public void showMenu() {
        int startX;
        int destX;
        if (oldDistanceY > 0) {
            startX = menuViewWidth;
            destX = 0;
            mState = DragState.Close;
        } else {
            startX = 0;
            destX = menuViewWidth;
            mState = DragState.Open;
        }

        oldDistanceY = destX;
        animationScroll(startX, destX);
    }
  • 側滑選單的自動歸位
    /**
     * 方法功能:自動歸位
     *
     * @param startX
     * @param destX
     */
    private void animationScroll(int startX, int destX) {
        int distanceX = destX - startX;
        int startY = 0;
        int distanceY = 0;
        float maxDuration = 1000f;
        float maxMoveDistance = menuViewWidth;
        float scale = maxDuration / maxMoveDistance;
        int duration = (int) (Math.abs(distanceX) - scale);

        scroller.startScroll(startX, startY, distanceX, distanceY, duration);

        invalidate();
    }

這裡道長用到了invalidate()這個方法重新繪製介面。但是需要注意的是:invalidate()方法不能直接在子執行緒中呼叫。Android UI操作並不是執行緒安全的,並且這些操作必須在UI執行緒中呼叫。子執行緒中重新整理介面可以通過Handler來通知UI執行緒呼叫invalidate()來進行介面更新。當然API提供了另一個方法來實現子執行緒中重新整理介面的方法postInvalidate()。使用postInvalidate()重新整理介面可以直接在子執行緒中呼叫postInvalidate()

到這裡側滑選單就實現了,這種實現方式與側滑選單實現的最大的區別在於事件攔截操作方式,希望這篇部落格可以給你提供一些幫助。下面是Demo的連結,如果需要可以去github下載

原始碼下載

SlidingMenu


相關文章