Android 事件分發實例之右滑結束Activity(二)

前言

本篇同上一篇實現同樣的功能,此篇採用之前音樂鎖屏壁紙的功能裏面使用的ViewDragHelper來實現,最終的效果與上篇效果相同,因此本篇只挑新內容講解。

具體步驟

事件分發與衝突解決

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean canScrollHorizontally = canScrollHorizontally(-1, this);
        if (!canScrollHorizontally) {
            return mViewDragHelper.shouldInterceptTouchEvent(ev);
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mViewDragHelper.processTouchEvent(event);
        return true;
    }

說明:上一篇已經說清一個事件裏面包含三個小事件,因爲此篇主要是把事件交由ViewDragHelper處理,因此處理canScrollHorizontally()方法時,判斷條件不能只放在Move事件中處理,否則Down、Up的事件ViewDragHelper接收不到,查看源碼知道mViewDragHelper.shouldInterceptTouchEvent(ev)和mViewDragHelper.processTouchEvent(event)方法必須要三個事件同時存在。

滑動處理

通過ViewDragHelper實現滑動效果,主要是通過實現ViewDragHelper的回調ViewDragHelper.Callback實現滑動效果。

        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return false;
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            //當前回調,鬆開手時觸發,比較觸發條件和當前的滑動距離
            int left = releasedChild.getLeft();
            if (left <= mMaxSlideWidth) {
                //緩慢滑動的方法,小於觸發條件,滾回去
                mViewDragHelper.settleCapturedViewAt(0, 0);
            } else {
                //大於觸發條件,滾出去...
                mViewDragHelper.settleCapturedViewAt(mScreenWidth, 0);
            }

            //需要手動調用更新界面的方法
            invalidate();
        }

        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            mScrollPercent = Math.abs((float) left
                    / (mScreenWidth + mEdgeShadow.getIntrinsicWidth()));
            //重繪
            invalidate();

            //當滾動位置到達屏幕最右邊,則關掉Activity
            if (changedView == mRootView && left >= mScreenWidth) {
                mActivity.finish();
            }
        }

        @Override
        public int getViewHorizontalDragRange(View child) {
            return ViewDragHelper.EDGE_LEFT;
        }

        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            //限制左右拖拽的位移
            left = left >= 0 ? left : 0;
            return left;
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            //上下不能移動,返回0
            return 0;
        }

        @Override
        public void onEdgeDragStarted(int edgeFlags, int pointerId) {
            //觸發邊緣時,主動捕捉mRootView
            mViewDragHelper.captureChildView(mRootView, pointerId);
        }

說明:1、tryCaptureView():由用戶決定捕獲哪個子View的行爲,由於我們是移動整個ViewGroup,直接返回false。
2、onViewReleased():當視圖拖拽完成時回調,主要處理鬆手之後視圖最終位置,mViewDragHelper.settleCapturedViewAt(0, 0)方法內部也是通過調用Scroller來實現移動
3、onViewPositionChanged():看方法名稱就知道View位置改變的時候回調,因此可以通過判斷最後的位置是否超過右邊緣來結束activity
4、getViewHorizontalDragRange():獲取當前水平方向的範圍,右滑,設置左邊緣即可
5、clampViewPositionHorizontal():限制水平方向的位置,默認不限制,因此需要重寫限制大於0,這樣就可以實現右滑
6、clampViewPositionVertical():限制垂直方向上的位置,右滑可以不重寫
7、onEdgeDragStarted():無子類捕獲,父類從之前設置的邊緣拖拽的時候回調,此時去捕獲activity的視圖

@Override
    public void computeScroll() {
        //使用settleCapturedViewAt方法是,必須重寫computeScroll方法,傳入true
        //持續滾動期間,不斷刷新ViewGroup
        if (mViewDragHelper.continueSettling(true))
            ViewCompat.postInvalidateOnAnimation(this);
    }

說明:mViewDragHelper.continueSettling(true)方法在拖拽過程中通過返回值不斷的回調,使拖拽過程更加平滑,與mViewDragHelper.settleCapturedViewAt(0, 0)呼應,主要還是利用Scroller的滑動機制。

觸摸範圍

完成上述步驟已經可以實現右滑,只是ViewDragHelper內部檢測是否是邊緣,然後才能去拖拽。ViewDragHelper源碼默認的邊緣是20dp,如果想實現全屏拖拽的效果必須修改此值,查看ViewDragHelper源碼並沒有可以修改邊緣寬度的方法,因此採用反射來實現。

/**
     * 設置可以拖拽的觸點範圍
     *
     * @param touchRange
     */
    private void setTouchRange(int touchRange) {
        Class<?> aClass = mViewDragHelper.getClass();
        Field mDividerHeight = null;
        try {
            mDividerHeight = aClass.getDeclaredField("mEdgeSize");
            mDividerHeight.setAccessible(true);
            mDividerHeight.setInt(mViewDragHelper, touchRange);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

總結

本篇採用與上篇不同的方式,主要不同點是事件處理的時機問題和觸摸範圍的修改,還有ViewDragHelper的各個回調方法處理,其他方面沒區別,現已完成測試,效果與上篇完全一致。現在還在做進一步的封裝,後續會持續更新最新代碼。
右滑結束Activity

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章