目錄
1.事件分發(fā)介紹
2.Down敢茁、up事件的分發(fā)過程
3.onTouchListener篡帕、onClickListener調(diào)用時機
4.事件攔截應(yīng)用
5.NestedScrollingParent
6.Behavior的使用
7.NestedScrollingChild接口來源
上面小節(jié)分析了時間傳遞的原理智哀,本節(jié)則主要介紹下他們的使用次询,分析需要事件攔截時應(yīng)如何處理,并介紹其原理瓷叫。即外部攔截法屯吊、內(nèi)部攔截法,代碼參考Android開發(fā)藝術(shù)探索一書摹菠,記錄心得并分析為什么這樣寫盒卸。
若ViewGroup中包裹一個View,此時滑動時需要處理之間的滑動沖突次氨,因此需要進行滑動事件的攔截蔽介,若果我們在ViewGroup中對事件進行攔截則叫外部攔截法,在View中進行事件的攔截則叫做內(nèi)部攔截法煮寡,下面主要討論Move事件的滑動攔截虹蓄。
1.外部攔截法
外部攔截法在ViewGroup中進行攔截,紅色字體為攔截條件幸撕,即當(dāng)y方向移動距離大于10是ViewGroup獲取滑動事件薇组。
我們主要在onInterceptTouchEvent中進行事件攔截,不攔截down事件坐儿,若攔截了down事件律胀,move事件也會在此處消耗,原因見http://www.reibang.com/p/7673bc4b5575挑童,up累铅、cancel事件我們不做攔截處理。
下面是我們外部攔截的事件傳遞圖站叼,從上節(jié)搬過來的娃兽。
2.內(nèi)部攔截法
內(nèi)部攔截法是在View中進行攔截。此時ViewGroup尽楔、View均需要進行處理投储。然后再View中通過
requestDisallowInterceptTouchEvent()函數(shù)來控制外層是否攔截。
看著這里我有幾個疑問:
(1).為什么父類不能攔截Down事件阔馋,requestDisallowInterceptTouchEvent方法是如何工作玛荞?
我們看下ViewGroup中dispatchTouchEvent的源碼
紅色字體就是子view通過requestDisallowInterceptTouchEvent方法設(shè)置的標(biāo)志位。
如果ViewGroup攔截了Down事件呕寝,那么move事件傳遞時勋眯,actionMasked != down && mFirstTouchTarget == null,因此此時便不會去訪問標(biāo)志位,那么我們在子View進行攔截也就失去了作用客蹋。此時塞蹭,若我們需要ViewGroup攔截Move事件,我們變設(shè)置requestDisallowInterceptTouchEvent(false)讶坯,此時disallowIntercept == false番电,便會調(diào)用ViewGroup的onInterceptTouchEvent(ev),因此我們需要在onInterceptTouchEvent返回true來進行攔截辆琅,故除去down事件外應(yīng)返回false漱办。
(2)為什么子View要在dispatchTouchEvent進行攔截呢,為什么不在onInterceptTouchEvent進行攔截呢婉烟?
因為View沒有OnInterceptTouchEvent函數(shù)娩井,只能在此方法攔截。
(3) 為什么我們平時使用requestDisallowInterceptTouchEvent()方法時隅很,我們并沒有重寫外部View撞牢,但是依然可以工作呢?
我們平時使用該方法時一般都是重寫子View在子View中控制叔营,而我們的外部View一般都是ScrollView、RecyclerView所宰、ViewPager等這種可以滑動的控件绒尊,而這些控件已經(jīng)幫我們實現(xiàn)了,他們都是默認不攔截down事件仔粥,攔截move事件的。下面簡單看下ScrollView的代碼。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
if (yDiff > mTouchSlop && (getNestedScrollAxes() & SCROLL_AXIS_VERTICAL) == 0) {
//此處開始滑動癣缅,開始攔截
mIsBeingDragged = true;
mLastMotionY = y;
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
mNestedYOffset = 0;
if (mScrollStrictSpan == null) {
mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
}
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
}
break;
}
//默認mIsBeingDragged = false
case MotionEvent.ACTION_DOWN: {
final int y = (int) ev.getY();
if (!inChild((int) ev.getX(), (int) y)) {
mIsBeingDragged = false;
recycleVelocityTracker();
break;
}
mLastMotionY = y;
mActivePointerId = ev.getPointerId(0);
initOrResetVelocityTracker();
mVelocityTracker.addMovement(ev);
mScroller.computeScrollOffset();
mIsBeingDragged = !mScroller.isFinished();
if (mIsBeingDragged && mScrollStrictSpan == null) {
mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
}
startNestedScroll(SCROLL_AXIS_VERTICAL);
break;
}
return mIsBeingDragged;
}
(4)由于事件傳遞是由ViewGroup傳入View安聘,因此該方法的攔截后第一個move會不會依然傳給view呢?
結(jié)果是會的麦向,log與流程圖均獻上瘟裸,有興趣可以看下。
android源碼中使用外部攔截法的較多诵竭,比如RecyclerView和ViewPager话告,在它們的onInterceptTouchEvent的Move事件中均可看出。
3.實例
此時若讓我們使用外部攔截法實現(xiàn)卵慰,開始View向下移動100dp沙郭,然后ViewGroup向下移動的場景呢?