問題描述
最近在做appbarlayout和recyclerView配合使用的時候废登,發(fā)現(xiàn)recyclerView和appbarlayout配合過程偶爾會非常的詭異础倍,特別是快速滑動的時候會導(dǎo)致appbarlayout突然彈開或者突然折疊,動畫都消失了∠叵埃總之用起來非常的不爽。為了解決這個問題谆趾,特意研究了快速滑動的時候到底干了些啥躁愿。
快速滑動介紹
不同于普通的滾動,快速滑動的時候觸發(fā)的函數(shù)和滾動函數(shù)是不一樣的沪蓬。普通的滾動只要計算你手指劃過的距離彤钟,然后將內(nèi)容滾多少就好了,手指拿開滾動就結(jié)束了跷叉。而快速滑動調(diào)用的是recycler內(nèi)部的fling函數(shù)样勃,是有速度和慣性的,手指滑動的距離只是用來計算加速度的性芬,就算手指離開了峡眶,視圖仍然會繼續(xù)滑動。那么Android內(nèi)部是怎么區(qū)分普通滑動和快速滑動呢植锉?一次觸摸是普通滑動和快速滑動并存生效的還是非此即彼的呢辫樱?接下去會詳細(xì)說明。
普通滾動機(jī)制
普通滑動在代碼上來說其實是一個drag事件俊庇,也就是拖動事件狮暑,也就是手不放開,一旦放開就不算drag了辉饱,也就結(jié)束了普通滑動搬男。我們知道一次觸摸事件解析成滾動事件是在onTouchEvent中完成的。正常的滾動事件是由ACTION_DOWN, ACTION_MOVE, ACTION_MOVE,.... ACTION_MOVE, ACTION_UP組成的彭沼。其中ACTION_MOVE的時候就會進(jìn)行普通的滑動操作缔逛,通過對MotionEvent中的dx,dy的數(shù)值來對界面進(jìn)行普通的滑動,也就是邊MOVE姓惑,界面邊變化的褐奴,這時候只要手指沒有放開(ACTION_UP事件)就沒有什么Fling的響應(yīng)。具體執(zhí)行就算下述代碼中scrollByInternal函數(shù)于毙。
public boolean onTouchEvent(MotionEvent e) {
//blabla...
switch (action) {
case MotionEvent.ACTION_DOWN: {
//嵌套滑動相應(yīng)分發(fā)...
} break;
case MotionEvent.ACTION_MOVE: {
//blabla判斷mScrollState是不是要變成SCROLL_STATE_DRAGGING
//blabla計算dx,dy
if (mScrollState == SCROLL_STATE_DRAGGING) {
mLastTouchX = x - mScrollOffset[0];
mLastTouchY = y - mScrollOffset[1];
if (scrollByInternal( //開始普通滑動
canScrollHorizontally ? dx : 0,
canScrollVertically ? dy : 0,
vtev)) {
getParent().requestDisallowInterceptTouchEvent(true);
}
}
} break;
case MotionEvent.ACTION_UP: {
//blabla敦冬,一些松開手指的操作,包括執(zhí)行fling
} break;
case MotionEvent.ACTION_CANCEL: {
cancelTouch();
} break;
}
//blabla 一些記錄滑動事件的操作
return true;}
快速滑動機(jī)制
- 快速滑動依賴于一個VelocityTracker的工具唯沮,每次的MotionEvent都會處理過以mVelocityTracker.addMovement(vtev)的方式記錄下來脖旱,讓后mVelocityTracker在內(nèi)部就會進(jìn)行計算用于控制下面一系列的快速滑動的判斷過程堪遂。
- 快速滑動是在ACTION_UP中被觸發(fā)的,根據(jù)mVelocityTracker來計算出需要快速滑動的距離萌庆,然后調(diào)用內(nèi)部的fling的方法溶褪,進(jìn)行相應(yīng)的快速滑動操作。注意快速滑動是一次性調(diào)用fling完成的踊兜,并不是像drag事件(普通滑動)一樣是ACTION_MOVE的時候一段一段拼起來的竿滨。看到的滾動效果是動畫出來的捏境。具體的見下述的onTouchEvent中的ACTION_UP一段于游。
public boolean onTouchEvent(MotionEvent e) {
//blabla...
switch (action) {
case MotionEvent.ACTION_DOWN: {
//嵌套滑動相應(yīng)分發(fā)...
} break;
case MotionEvent.ACTION_MOVE: {
//blabla 普通滑動drag操作
} break;
case MotionEvent.ACTION_UP: {
mVelocityTracker.addMovement(vtev); //記錄本次滑動事件,表示彈起
eventAddedToVelocityTracker = true;
mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
final float xvel = canScrollHorizontally ? //計算水平fling的距離
-VelocityTrackerCompat.getXVelocity(mVelocityTracker, mScrollPointerId) : 0;
final float yvel = canScrollVertically ? //計算豎直fling的距離
-VelocityTrackerCompat.getYVelocity(mVelocityTracker, mScrollPointerId) : 0;
if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) { //執(zhí)行快速滑動fling
setScrollState(SCROLL_STATE_IDLE);
}
resetTouch();
} break;
case MotionEvent.ACTION_CANCEL: {
cancelTouch();
} break;
}
if (!eventAddedToVelocityTracker) {
mVelocityTracker.addMovement(vtev); //添加本次的滑動事件垫言,并計算
}
vtev.recycle(); //釋放內(nèi)存
return true;}