CoordinatorLayout渣慕、Behavior和嵌套滑動(一)

嵌套滑動

一個CoordinatorLayout嵌套NestedScrollView :點擊之后彈起

首先調(diào)用CoordinatorLayout的onInterceptTouchEvent()函數(shù)语卤,返回false。之后調(diào)用NestedScrollView的onInterceptTouchEvent()霜威。

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
            
            ......

            case MotionEvent.ACTION_DOWN: {
                final int y = (int) ev.getY();
                if (!inChild((int) ev.getX(), (int) y)) {
                    mIsBeingDragged = false;
                    recycleVelocityTracker();
                    break;
                }

                /*
                 * Remember location of down touch.
                 * ACTION_DOWN always refers to pointer index 0.
                 */
                mLastMotionY = y;
                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);

                initOrResetVelocityTracker();
                mVelocityTracker.addMovement(ev);
                /*
                 * If being flinged and user touches the screen, initiate drag;
                 * otherwise don't. mScroller.isFinished should be false when
                 * being flinged. We need to call computeScrollOffset() first so that
                 * isFinished() is correct.
                */
                mScroller.computeScrollOffset();
                mIsBeingDragged = !mScroller.isFinished();
                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
                break;
            }

            ......
        }

        /*
        * The only time we want to intercept motion events is if we are in the
        * drag mode.
        */
        return mIsBeingDragged;
    }   

這里先看CoordinatorLayout對 ACTION_DOWN的處理谈喳。首先判斷該觸摸點是否在NestedScrollView的范圍內(nèi),如果在戈泼,則繼續(xù)婿禽。這里用到VelocityTracker這個類,主要用于通過跟蹤一連串事件實時計算出當前的速度大猛。在這次點擊中NestedScrollView并沒有在滾動扭倾,所以mScroller.isFinished()是返回true的,所以mIsBeingDragged為false挽绩,此次動作沒有攔截膛壹。在返回之前調(diào)用了startNestedScroll()這個方法。

    @Override
    public boolean startNestedScroll(int axes) {
        return mChildHelper.startNestedScroll(axes);
    } 

可以看到在這里調(diào)用了NestedScrollingChildHelper?的startNestedScroll()方法唉堪。

    public boolean startNestedScroll(int axes) {
        if (hasNestedScrollingParent()) {
            // Already in progress
            return true;
        }
        // 尋找是否有可以進行嵌套滑動的父布局
        if (isNestedScrollingEnabled()) {
            ViewParent p = mView.getParent();
            View child = mView;
            while (p != null) {
                if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes)) {
                    mNestedScrollingParent = p;
                    ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes);
                    return true;
                }
                if (p instanceof View) {
                    child = (View) p;
                }
                p = p.getParent();
            }
        }
        return false;
    } 

最開始先判斷是否有可以進行嵌套滑動的父布局模聋,只要正在進行嵌套滑動,這個函數(shù)就會返回true唠亚,但一開始是返回false的链方。之后有一個循環(huán),尋找是否有可以處理嵌套滑動的父布局灶搜。在循環(huán)過程中祟蚀,child一直是parent的直接子View,而且child一直包含mView割卖。

public boolean hasNestedScrollingParent() {
        return mNestedScrollingParent != null;
    } 

之后調(diào)用ViewParentCompat.onStartNestedScroll(p, child, mView, axes)前酿。如果返回true,則該可滾動的視圖有可嵌套滑動的父布局究珊。

    public static boolean onStartNestedScroll(ViewParent parent, View child, View target,
            int nestedScrollAxes) {
        return IMPL.onStartNestedScroll(parent, child, target, nestedScrollAxes);
    }? 

這里的target是觸發(fā)嵌套滑動的可滾動的View薪者,chid是Parent的直接子View,它包含target剿涮。這里我們NestedScrollView直接就是CoordinatorLayout的子View言津,所以child和target是同一個攻人。IMPL用來處理兼容性。

    static final ViewParentCompatImpl IMPL;
    static {
        final int version = Build.VERSION.SDK_INT;
        if (version >= 21) {
            IMPL = new ViewParentCompatLollipopImpl();
        } else if (version >= 19) {
            IMPL = new ViewParentCompatKitKatImpl();
        } else if (version >= 14) {
            IMPL = new ViewParentCompatICSImpl();
        } else {
            IMPL = new ViewParentCompatStubImpl();
        }
    }

這里來看其中一個就好:

static class ViewParentCompatStubImpl implements ViewParentCompatImpl {
        @Override
        public boolean requestSendAccessibilityEvent(
                ViewParent parent, View child, AccessibilityEvent event) {
            // Emulate what ViewRootImpl does in ICS and above.
            if (child == null) {
                return false;
            }
            final AccessibilityManager manager = (AccessibilityManager) child.getContext()
                    .getSystemService(Context.ACCESSIBILITY_SERVICE);
            manager.sendAccessibilityEvent(event);
            return true;
        }

        @Override
        public boolean onStartNestedScroll(ViewParent parent, View child, View target,
                int nestedScrollAxes) {
            if (parent instanceof NestedScrollingParent) {
                return ((NestedScrollingParent) parent).onStartNestedScroll(child, target,
                        nestedScrollAxes);
            }
            return false;
        }
        ...... 

先看onStartNestedScroll()這個方法悬槽。這里面判斷parent是不是NestedScrollingParent的一個實例怀吻。因為CoordinatorLayout繼承了NestedScrollingParent, 所以最終調(diào)用了CoordinatorLayout的onStartNestedScroll()方法。

public class CoordinatorLayout extends ViewGroup implements NestedScrollingParent{
    ......
}

CoordinatorLayout的onStartNestedScroll()方法:

  public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        boolean handled = false;

        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View view = getChildAt(i);
            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
            final Behavior viewBehavior = lp.getBehavior();
            if (viewBehavior != null) {
                final boolean accepted = viewBehavior.onStartNestedScroll(this, view, child, target,
                        nestedScrollAxes);
                handled |= accepted;

                lp.acceptNestedScroll(accepted);
            } else {
                lp.acceptNestedScroll(false);
            }
        }
        return handled;
    } 

在onStartNestedScroll()方法中會判斷子View的LayoutParams是否設(shè)置了Behavior屬性初婆,如果都沒有蓬坡,則直接返回false。這里我們的子View沒有設(shè)置Behavior磅叛,所以返回了false屑咳。

之后動作傳到onTouchEvent()。

   @Override
    public boolean onTouchEvent(MotionEvent ev) {
        initVelocityTrackerIfNotExists();

        MotionEvent vtev = MotionEvent.obtain(ev);

        final int actionMasked = MotionEventCompat.getActionMasked(ev);

        if (actionMasked == MotionEvent.ACTION_DOWN) {
            mNestedYOffset = 0;
        }
        vtev.offsetLocation(0, mNestedYOffset);

        switch (actionMasked) {
            case MotionEvent.ACTION_DOWN: {
                if (getChildCount() == 0) {
                    return false;
                }
                if ((mIsBeingDragged = !mScroller.isFinished())) {
                    final ViewParent parent = getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                }

                /*
                 * If being flinged and user touches, stop the fling. isFinished
                 * will be false if being flinged.
                 */
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                }

                // Remember where the motion event started
                mLastMotionY = (int) ev.getY();
                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
                break;
            }
            ...... 

同樣也會調(diào)用startNestedScroll(), 之后的流程和上面的一樣了弊琴,但是這里onTouchEvent()會返回true兆龙。到最后是ACTION_UP。

          ......
            case MotionEvent.ACTION_UP:
                if (mIsBeingDragged) {
                    final VelocityTracker velocityTracker = mVelocityTracker;
                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                    int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(velocityTracker,
                            mActivePointerId);

                    if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
                        flingWithNestedDispatch(-initialVelocity);
                    } else if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
                            getScrollRange())) {
                        ViewCompat.postInvalidateOnAnimation(this);
                    }
                }
                mActivePointerId = INVALID_POINTER;
                endDrag();
                break;
                ...... 

這里mIsBeingDragged一直為false敲董,先不管紫皇。看endDrag()腋寨。

  private void endDrag() {
        mIsBeingDragged = false;

        recycleVelocityTracker();
        stopNestedScroll();

        if (mEdgeGlowTop != null) {
            mEdgeGlowTop.onRelease();
            mEdgeGlowBottom.onRelease();
        }
    }

調(diào)用了stopNestedScroll()聪铺。

   @Override
    public void stopNestedScroll() {
        mChildHelper.stopNestedScroll();
    } 

NestedScrollingChildHelper?。

  public void stopNestedScroll() {
        if (mNestedScrollingParent != null) {
            ViewParentCompat.onStopNestedScroll(mNestedScrollingParent, mView);
            mNestedScrollingParent = null;
        }
    }

這里mNestedScrollingParent為空萄窜,所以不會調(diào)用到CoordinatorLayout的onNestedStopScroll()铃剔。

NestedScrollView在onInterceptTouchEvent()和onTouchEvent()中都會調(diào)用startNestedScroll(),最后會調(diào)用到CoordinatorLayout的onStartNestedScroll()脂倦。但是如果CoordinatorLayout的子View都沒有設(shè)置Behavior番宁,則什么都不會發(fā)生。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赖阻,一起剝皮案震驚了整個濱河市蝶押,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌火欧,老刑警劉巖棋电,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異苇侵,居然都是意外死亡赶盔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門榆浓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來于未,“玉大人,你說我怎么就攤上這事『嫫郑” “怎么了抖坪?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長闷叉。 經(jīng)常有香客問我擦俐,道長,這世上最難降的妖魔是什么握侧? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任蚯瞧,我火速辦了婚禮,結(jié)果婚禮上品擎,老公的妹妹穿的比我還像新娘埋合。我一直安慰自己饥悴,他們只是感情好,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布瓣铣。 她就那樣靜靜地躺著答朋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪棠笑。 梳的紋絲不亂的頭發(fā)上梦碗,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機與錄音蓖救,去河邊找鬼洪规。 笑死,一個胖子當著我的面吹牛循捺,可吹牛的內(nèi)容都是我干的斩例。 我是一名探鬼主播念赶,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼恰力!你這毒婦竟也來了叉谜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤踩萎,失蹤者是張志新(化名)和其女友劉穎停局,沒想到半個月后码倦,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡裆泳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年叹洲,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片工禾。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡运提,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出闻葵,到底是詐尸還是另有隱情民泵,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布槽畔,位于F島的核電站栈妆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏厢钧。R本人自食惡果不足惜鳞尔,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望早直。 院中可真熱鬧寥假,春花似錦、人聲如沸霞扬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喻圃。三九已至萤彩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間斧拍,已是汗流浹背雀扶。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留肆汹,地道東北人怕吴。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像县踢,于是被迫代替她去往敵國和親转绷。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354