我的CSDN博客同步發(fā)布:ViewPager源碼分析(2):滑動及沖突處理
轉(zhuǎn)載請注明出處:【huachao1001的簡書:http://www.reibang.com/users/0a7e42698e4b/latest_articles】
上一篇介紹了ViewPager
的onMeasure
和onLayout
兩個方法,這是自定義View最基本的兩個函數(shù)硝逢。但是我們的ViewPager
有個需求就是滑動姨拥,接下來我們一起去學(xué)習(xí)ViewPager
在滑動方面做了哪些工作,以及ViewPager
如何處理與子View
之間的滑動沖突渠鸽。由于ViewPager的子View有Decor View還有普通的子View叫乌,而本篇文章講的主要是普通子View,因此徽缚,不再去刻意區(qū)分憨奸,以下所說的子View不包括DecorView。
1 Scroller典型用法
我們知道猎拨,Android
內(nèi)置了Scroller
對象膀藐,用于實現(xiàn)漸近式的滑動屠阻。假設(shè)我們自定義一個函數(shù)smoothScrollTo(int destX,int destY)
,用于讓ViewPager
漸近式的滑動到(destX,destY)
這個坐標位置额各,那么使用Scroller
實現(xiàn)步驟一般如下:
- 創(chuàng)建Scroller對象:
Scroller scroller=new Scroller(context);
- 重寫
computeScroll()
方法- 最后国觉,在我們的
smoothScrollTo
方法中調(diào)用startScroll
方法
參考如下代碼:
@Override
public void computeScroll(){
if(scroller.computeScrollOffset()){
scrollTo(scroller.getCurrX(),scroller.getCurrY());
postInvalidate();
}
}
public void smoothScrollTo(int destX,int destY){
int scrollX=getScrollX();
int deltaX=destX-scrollX;
scroller.startScroll(scrollX,0,deltaX,0,1000);
}
以上的smoothScrollTo實現(xiàn)的是x方向的平滑,其中startScroll函數(shù)的形參分別表示:起始位置的x坐標虾啦、起始位置的y坐標麻诀、x方向要移動的距離、y方向上要移動的距離以及整個滑動過程完成所需的時間傲醉。
2 ViewPager滑動
2.1 ViewPager定義Scroller
參照我們上一節(jié)提到的Scroller典型用法蝇闭,我們進入到ViewPager源碼。我們在ViewPager的initViewPager方法中找到:
void initViewPager() {
//····
final Context context = getContext();
mScroller = new Scroller(context, sInterpolator);
//····
}
它跟我們上一節(jié)使用到的Scroller構(gòu)造器不同硬毕,他選擇使用2個形參的構(gòu)造器呻引。其實,第二個形參就是插值器(interpolator
)吐咳,對插值器不熟悉的童鞋可以去搜索一下動畫插值器相關(guān)內(nèi)容逻悠。其實這個插值器就是根據(jù)不同的時間控制滑動的速度,就像高中物理中的物體變速運動韭脊。我們繼續(xù)看看ViewPager
中自定義的插值器sInterpolator
童谒,從變量名稱中以s開頭,就知道sInterpolator
是個static屬性:
private static final Interpolator sInterpolator = new Interpolator() {
public float getInterpolation(float t) {
t -= 1.0f;
return t * t * t * t * t + 1.0f;
}
};
Interpolator是一個接口沪羔,它繼承自TimeInterpolator這個接口饥伊,而Interpolator沒有添加新的抽象方法,TimeInterpolator只有一個抽象方法:float getInterpolation(float input);
其中蔫饰,input形參是取值范圍為0到1琅豆,表示當前的動畫時間點,0表示動畫開始死嗦,1表示動畫結(jié)束趋距。返回值表示移動到目標位置的比值粒氧,如果大于1越除,則表示超出了最大位置,小于0表示比最小位置還要小外盯。怎么理解呢摘盆?舉個例子,假設(shè)我們要實現(xiàn)變速動畫饱苟,我們要持續(xù)的時間是[0,1000]孩擂,要滑動的距離是[0,100],那么假設(shè)當前時間是200箱熬,則傳入到getInterpolation的形參就是200/1000=0.2类垦,表示時間過了0.2狈邑,具體的返回值可以根據(jù)你的變速需求計算,假設(shè)你的返回值是0.8蚤认,那么表示當前位置要處于100 * 0.8=80這個位置米苹。如果你的返回值是1.8 ,那么肯定就是超出100了:100*1.8=180。
2.2 ViewPager重寫computeScroll()方法
ViewPager實現(xiàn)的功能已經(jīng)兼容性都是比較健全的砰琢,所有computeScroll()不會像我們所寫的那么簡單蘸嘶,我們一起"膜拜"一下官方代碼吧:
@Override
public void computeScroll() {
//1.mIsScrollStarted標記當前在滑動
mIsScrollStarted = true;
//2.確保mScroller還沒有結(jié)束計算滑動位置
if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
//3.保存當前所處的位置oldX,oldY
int oldX = getScrollX();
int oldY = getScrollY();
//4.取出由mScroller計算出來的位置
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();
//5.只要x和y方向有一個發(fā)生了變化,就去滾動
if (oldX != x || oldY != y) {
//6.滑到mScroller計算出來的新位置
scrollTo(x, y);
//7.調(diào)用pageScrolled陪汽,只有當ViewPager里面沒有子View才會返回false
if (!pageScrolled(x)) {
//8.結(jié)束動畫训唱,并使得當前位置處于最終的位置
mScroller.abortAnimation();
//9.沒有子View,說明x方向無需滑動挚冤,再次確保y方向滑動
scrollTo(0, y);
}
}
// 10.不斷的postInvalidate,使得不斷重繪况增,達到動畫效果
ViewCompat.postInvalidateOnAnimation(this);
return;
}
//11.做一些滑動結(jié)束后的相關(guān)操作
// 注意到,上面的if里面有個return训挡,也就是說巡通,
// 只要是在滑動,就不會執(zhí)行到下面的代碼舍哄,
// 反之宴凉,執(zhí)行到下面代碼就說明已經(jīng)滑動結(jié)束
completeScroll(true);
}
computeScroll
函數(shù)里面大部分代碼比較清晰,只有兩個函數(shù)表悬,需要我們進去深究:pageScrolled
以及completeScroll
弥锄。
2.2.1 pageScrolled
先看看pageScrolled
函數(shù),這個函數(shù)主要的作用是回調(diào)onPageScrolled
蟆沫,雖然做了很多計算籽暇,但這些計算的結(jié)果最終是為了作為形參傳給onPageScrolled
,看看他的源碼:
private boolean pageScrolled(int xpos) {
//1.mItems是ArrayList類型饭庞,它保存的是每個子View的抽象描述類ItemInfo
//如果沒有子View
if (mItems.size() == 0) {
//2.先認為沒有調(diào)用父類
//mCalledSuper作用是:如果子類重寫了onPageScrolled戒悠,
// 那么子類的實現(xiàn)必須要先調(diào)用父類ViewPager的onPageScrolled
//為了確保子類的實現(xiàn)中先調(diào)用了父類ViewPager的onPageScrolled,定義了mCalledSuper
//并且在ViewPager類中的onPageScrolled將mCalledSuper設(shè)置為了true舟山,用于判斷子類有沒有調(diào)用绸狐。
mCalledSuper = false;
//3.調(diào)用onPageScrolled,如果子類重寫了該方法累盗,調(diào)用的則是子類的onPageScrolled
onPageScrolled(0, 0, 0);
//4.如果沒有執(zhí)行ViewPager的onPageScrolled,拋出異常
if (!mCalledSuper) {
throw new IllegalStateException(
"onPageScrolled did not call superclass implementation");
}
//5.如果沒有子View寒矿,返回false
return false;
}
//6.根據(jù)當前滑動的位置,得到當前顯示的子View的抽象描述類ItemInfo
//只要存在子View若债,得到的ItemInfo對象肯定不為null
final ItemInfo ii = infoForCurrentScrollPosition();
//7.獲取顯示區(qū)域的寬度
final int width = getClientWidth();
//8.加上外邊距后的寬度
final int widthWithMargin = width + mPageMargin;
final float marginOffset = (float) mPageMargin / width;
//保存當前是第幾個頁面(即第幾個子View)
final int currentPage = ii.position;
//計算當前頁面的偏移量符相,取值為[0,1),如果pageOffset不等于0,則下一個頁面可見
final float pageOffset = (((float) xpos / width) - ii.offset) /
(ii.widthFactor + marginOffset);
//當前頁面移動的像素點個數(shù)
final int offsetPixels = (int) (pageOffset * widthWithMargin);
//以下作用與2蠢琳、3啊终、4類似
mCalledSuper = false;
onPageScrolled(currentPage, pageOffset, offsetPixels);
if (!mCalledSuper) {
throw new IllegalStateException(
"onPageScrolled did not call superclass implementation");
}
return true;
}
我們定位到第6個注釋镜豹,我提到infoForCurrentScrollPosition
函數(shù)是據(jù)當前滑動的位置,得到當前顯示的子View的抽象描述類ItemInfo蓝牲,如果當前滑動位置顯示的恰好是一個完整的頁面逛艰,這個頁面的前一個頁面和后一個頁面都沒有顯示,那么很容易理解搞旭,返回的就是這個頁面散怖。可是如果當前顯示區(qū)域是同時顯示2個頁面(兩個頁面都顯示一部分出現(xiàn)在顯示區(qū)域)肄渗,那這個函數(shù)應(yīng)該返回哪一個頁面呢镇眷?從infoForCurrentScrollPosition
源碼看出每次是返回左邊的頁面,如下圖所示:
換句話說翎嫡,只會是存在當前頁面與下一個頁面同時出現(xiàn)在顯示區(qū)域欠动,不可能是當前頁面與上一個頁面同時出現(xiàn)。關(guān)于infoForCurrentScrollPosition
的具體實現(xiàn)惑申,我們不要去關(guān)心具伍,我們只要知道它幫我們實現(xiàn)了什么功能,如果對其感興趣可以去看源碼圈驼。
2.2.2 onPageScrolled
上面我們知道人芽,pageScrolled
函數(shù)是為了調(diào)用onPageScrolled
做前期計算,并將計算結(jié)果作為onPageScrolled
的形參绩脆,最終是為了回調(diào)onPageScrolled
函數(shù)萤厅,那么我們看看onPageScrolled
函數(shù)到底是干了啥~,從函數(shù)名看的出來靴迫,它是一個回調(diào)函數(shù)惕味,那么是什么情況下回調(diào)呢?其實玉锌,在我們手指滑動或者是通過代碼直接滑動到指定位置過程中名挥,會使得一些頁面滑動,如果我們想要在每個頁面在顯示區(qū)域滑動過程中實現(xiàn)某些效果主守,可以重寫這個函數(shù)禀倔,當然了,我們前面分析pageScrolled
函數(shù)時就提到丸逸,重寫onPageScrolled
時蹋艺,必須先調(diào)用super.onPageScrolled(position, offset, offsetPixels)
剃袍,我們的ViewPager在滑動過程中黄刚,會不斷回調(diào)onPageScrolled函數(shù),這個“不斷”是從這里體現(xiàn):computeScroll—>onPageScrolled->onPageScrolled民效°疚滑動過程不斷調(diào)用computeScroll
涛救,而computeScroll
調(diào)用onPageScrolled
,onPageScrolled
又調(diào)用onPageScrolled
业扒。好了检吆,我們?nèi)タ纯?code>onPageScrolled吧~首先看看三個參數(shù):
int position
,表示當前是第幾個頁面float offset
表示當前頁面移動的距離程储,其實就是個相對實際寬度比例值蹭沛,取值為[0,1)。0表示整個頁面在顯示區(qū)域章鲤,1表示整個頁面已經(jīng)完全左移出顯示區(qū)域摊灭。int offsetPixels
, 表示當前頁面左移的像素個數(shù)。
我們已經(jīng)了解形參的含義败徊,接下來看看源碼:
@CallSuper
protected void onPageScrolled(int position, float offset, int offsetPixels) {
// Offset any decor views if needed - keep them on-screen at all times.
//1.如果有Decor View帚呼,則需要使得它們時刻顯示在屏幕中,不移出屏幕
if (mDecorChildCount > 0) {
//根據(jù)Gravity將Decor View擺放到指定位置皱蹦,注釋略煤杀,可以參考上一篇文章
//代碼略···
}
//2.分發(fā)頁面滾動事件
dispatchOnPageScrolled(position, offset, offsetPixels);
//3.如果mPageTransformer不為null,則不斷去調(diào)用mPageTransformer的transformPage函數(shù)
if (mPageTransformer != null) {
final int scrollX = getScrollX();
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
//只針對頁面進行處理
if (lp.isDecor) continue;
//計算child位置
final float transformPos = (float) (child.getLeft() - scrollX) / getClientWidth();
//調(diào)用transformPage
mPageTransformer.transformPage(child, transformPos);
}
}
//標記ViewPager的onPageScrolled函數(shù)執(zhí)行過
mCalledSuper = true;
}
從源碼上我們知道沪哺,onPageScrolled
做了3件事沈自,首先把Decor View
固定在顯示區(qū)域,其次辜妓,將滾動事件進行分發(fā)酥泛,即dispatchOnPageScrolled
函數(shù),dispatchOnPageScrolled
函數(shù)內(nèi)部就是調(diào)用OnPageChangeListener
的onPageScrolled
函數(shù)嫌拣,我們添加的監(jiān)聽器就是此時被回調(diào)onPageScrolled
函數(shù)柔袁,dispatchOnPageScrolled
函數(shù)代碼比較簡單,不去追究异逐。最后捶索,就是判斷是否設(shè)置了mPageTransformer
,如果設(shè)置了灰瞻,就去回調(diào)mPageTransformer
的transformPage
函數(shù)腥例,我們知道,我們可以通過自定義PageTransformer
來實現(xiàn)每個頁面的“出場動畫”和“離場動畫”酝润,就是這里回調(diào)transformPage
來實現(xiàn)的燎竖。
2.2.3 completeScroll
把目光回到computeScroll
函數(shù),我們前面說道要销,在computeScroll
函數(shù)最后調(diào)用了completeScroll
函數(shù)构回,這個函數(shù)是做滑動結(jié)束后的清理復(fù)位等工作。比如:確保滾動已經(jīng)到最終位置,如果沒有到最終位置纤掸,則滾動到最終位置脐供。還有就是將每個頁面對應(yīng)的ItemInfo
對象的scrolling
設(shè)為false
等等。
2.3 ViewPager 定義smoothScrollTo函數(shù)
根據(jù)第1節(jié)借跪,我們知道政己,重寫了computeScroll
函數(shù)后,需要自定義一種平滑到指定位置的函數(shù)掏愁,一般命名為smoothScrollTo
歇由,當然咯,你也可以取其他名字果港,你開心就好~印蓖。但是在這個函數(shù)里面需要調(diào)用startScroll
函數(shù)。我們來看看ViewPager
的smoothScrollTo
函數(shù)源碼京腥,其中x,y
表示要移動到的位置赦肃,velocity
表示手指移動速度,如果不是用戶的手指觸發(fā)的平滑操作公浪,則velocity
設(shè)為0即可:
void smoothScrollTo(int x, int y, int velocity) {
if (getChildCount() == 0) {
// 如果沒有頁面他宛,啥也不干
setScrollingCacheEnabled(false);
return;
}
//定義x軸起始位置
int sx;
//判斷在此之前mScroller是否還在計算滾動
boolean wasScrolling = (mScroller != null) && !mScroller.isFinished();
//如果當前在滾動
if (wasScrolling) {
//根據(jù)在此之前是否還在滾動來決定如何獲取當前的x位置
sx = mIsScrollStarted ? mScroller.getCurrX() : mScroller.getStartX();
// 如果mScroller在此之前還在計算滾動,則將其停止計算欠气,并直接滑動到最終位置厅各,
// 這個最終位置即為此刻smoothScrollTo的起始位置
mScroller.abortAnimation();
//不啟用緩存
setScrollingCacheEnabled(false);
} else {//如果當前滾動結(jié)束
sx = getScrollX();
}
//獲取y軸起始位置
int sy = getScrollY();
//計算要移動的x和y方向的距離
int dx = x - sx;
int dy = y - sy;
//如果x和y方向的移動距離都是0,說明無需移動预柒,結(jié)束并返回
if (dx == 0 && dy == 0) {
//做一些清理和還原工作
completeScroll(false);
//已經(jīng)確定好新的頁面队塘,將mCurItem設(shè)置為新的頁面以及其他的相關(guān)處理
populate();
//設(shè)置當前的滾動狀態(tài)
setScrollState(SCROLL_STATE_IDLE);
return;
}
//啟用緩存,即對每個子View調(diào)用setDrawingCacheEnabled(true)
setScrollingCacheEnabled(true);
//設(shè)置當前的滾動狀態(tài)
setScrollState(SCROLL_STATE_SETTLING);
//獲取寬度及一半寬度
final int width = getClientWidth();
final int halfWidth = width / 2;
//要移動的距離占寬度的比例宜鸯,這個比例必須得小于等于1
final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);
//smoothScrollTo并沒有使用勻速滑動憔古,而是通過distanceInfluenceForSnapDuration函數(shù)
//來實現(xiàn)變速,這里與Scroller里面的插值器之間并無影響
final float distance = halfWidth + halfWidth *
distanceInfluenceForSnapDuration(distanceRatio);
int duration;
velocity = Math.abs(velocity);
//如果手指滑動速度不為0
if (velocity > 0) {
//如果是手指滑動淋袖,則需要根據(jù)手指滑動速度計算滑動持續(xù)時間
duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
} else {
//如果手指滑動速度為0鸿市,即,是通過代碼的方式滑動到指定位置即碗,則使用另一種方式計算滑動持續(xù)時間
final float pageWidth = width * mAdapter.getPageWidth(mCurItem);
final float pageDelta = (float) Math.abs(dx) / (pageWidth + mPageMargin);
duration = (int) ((pageDelta + 1) * 100);
}
//確保整個滑動時間不超出最大的時間
duration = Math.min(duration, MAX_SETTLE_DURATION);
//將mIsScrollStarted標記重置為false焰情,表示沒有開始滾動,
//這個標記會在computeScrollOffset函數(shù)中重置為true剥懒,
//所以不用擔心會影響到其他地方的判斷
mIsScrollStarted = false;
//開始平滑
mScroller.startScroll(sx, sy, dx, dy, duration);
ViewCompat.postInvalidateOnAnimation(this);
}
從上面可以看到内舟,ViewPager
的smoothScrollTo
的實現(xiàn)還是挺復(fù)雜的,代碼實現(xiàn)出來的效果體驗非常好以及所考慮的功能很全面初橘。感覺非常值得去學(xué)習(xí)验游!另外充岛,ViewPager提供了只有x
,y
兩個參數(shù)的smoothScrollTo
,其內(nèi)部也是調(diào)用上面這個smoothScrollTo
批狱,只是將velocity
參數(shù)設(shè)置為0裸准。
3 滑動沖突
現(xiàn)在為止展东,ViewPager
的滑動部分已經(jīng)分析完畢赔硫,但是用過ViewPager
都知道,ViewPager
幫我們處理了滑動沖突盐肃。我們知道爪膊,ViewPager
只關(guān)注水平方向的手指滑動,根據(jù)水平方向的手指滑動來切換頁面砸王。在垂直方向上推盛,ViewPager
并不關(guān)心,因此谦铃,ViewPager
很有必要解決一下滑動沖突耘成,把豎直方向的滑動傳遞給子View來處理。
我們知道驹闰,ViewGroup
是在onInterceptTouchEvent
函數(shù)中決定是否攔截觸摸事件瘪菌,那么我們就去學(xué)習(xí)一下ViewPager
的onInterceptTouchEvent
函數(shù)。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//1. 觸摸動作
final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
//2. 時刻要注意觸摸是否已經(jīng)結(jié)束
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
//3. Release the drag.
if (DEBUG) Log.v(TAG, "Intercept done!");
//4. 重置一些跟判斷是否攔截觸摸相關(guān)變量
resetTouch();
//5. 觸摸結(jié)束嘹朗,無需攔截
return false;
}
//6. 如果當前不是按下事件师妙,我們就判斷一下,是否是在拖拽切換頁面
if (action != MotionEvent.ACTION_DOWN) {
//7. 如果當前是正在拽切換頁面屹培,直接攔截掉事件默穴,后面無需再做攔截判斷
if (mIsBeingDragged) {
if (DEBUG) Log.v(TAG, "Intercept returning true!");
return true;
}
//8. 如果標記為不允許拖拽切換頁面,我們就"放過"一切觸摸事件
if (mIsUnableToDrag) {
if (DEBUG) Log.v(TAG, "Intercept returning false!");
return false;
}
}
//9. 根據(jù)不同的動作進行處理
switch (action) {
//10. 如果是手指移動操作
case MotionEvent.ACTION_MOVE: {
//11. 代碼能執(zhí)行到這里褪秀,就說明mIsBeingDragged==false蓄诽,否則的話,在第7個注釋處就已經(jīng)執(zhí)行結(jié)束了
//12.使用觸摸點Id媒吗,主要是為了處理多點觸摸
final int activePointerId = mActivePointerId;
if (activePointerId == INVALID_POINTER) {
//13.如果當前的觸摸點id不是一個有效的Id若专,無需再做處理
break;
}
//14.根據(jù)觸摸點的id來區(qū)分不同的手指,我們只需關(guān)注一個手指就好
final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);
//15.根據(jù)這個手指的序號蝴猪,來獲取這個手指對應(yīng)的x坐標
final float x = MotionEventCompat.getX(ev, pointerIndex);
//16.在x軸方向上移動的距離
final float dx = x - mLastMotionX;
//17.x軸方向的移動距離絕對值
final float xDiff = Math.abs(dx);
//18.同理调衰,參照16、17條注釋
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float yDiff = Math.abs(y - mInitialMotionY);
if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
//19.判斷當前顯示的頁面是否可以滑動自阱,如果可以滑動嚎莉,則將該事件丟給當前顯示的頁面處理
//isGutterDrag是判斷是否在兩個頁面之間的縫隙內(nèi)移動
//canScroll是判斷頁面是否可以滑動
if (dx != 0 && !isGutterDrag(mLastMotionX, dx) &&
canScroll(this, false, (int) dx, (int) x, (int) y)) {
mLastMotionX = x;
mLastMotionY = y;
//20.標記ViewPager不去攔截事件
mIsUnableToDrag = true;
return false;
}
//21.如果x移動距離大于最小距離募书,并且斜率小于0.5照激,表示在水平方向上的拖動
if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {
if (DEBUG) Log.v(TAG, "Starting drag!");
//22.水平方向的移動,需要ViewPager去攔截
mIsBeingDragged = true;
//23.如果ViewPager還有父View铆隘,則還要向父View申請將觸摸事件傳遞給ViewPager
requestParentDisallowInterceptTouchEvent(true);
//24.設(shè)置滾動狀態(tài)
setScrollState(SCROLL_STATE_DRAGGING);
//25.保存當前位置
mLastMotionX = dx > 0 ? mInitialMotionX + mTouchSlop :
mInitialMotionX - mTouchSlop;
mLastMotionY = y;
//26.啟用緩存
setScrollingCacheEnabled(true);
} else if (yDiff > mTouchSlop) {//27.否則的話,表示是豎直方向上的移動
if (DEBUG) Log.v(TAG, "Starting unable to drag!");
//28.豎直方向上的移動則不去攔截觸摸事件
mIsUnableToDrag = true;
}
if (mIsBeingDragged) {
// 29.跟隨手指一起滑動
if (performDrag(x)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
break;
}
//30.如果手指是按下操作
case MotionEvent.ACTION_DOWN: {
//31.記錄按下的點位置
mLastMotionX = mInitialMotionX = ev.getX();
mLastMotionY = mInitialMotionY = ev.getY();
//32.第一個ACTION_DOWN事件對應(yīng)的手指序號為0
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
//33.重置允許拖拽切換頁面
mIsUnableToDrag = false;
//34.標記開始滾動
mIsScrollStarted = true;
//35.手動調(diào)用計算滑動的偏移量
mScroller.computeScrollOffset();
//36.如果當前滾動狀態(tài)為正在將頁面放置到最終位置叫确,
//且當前位置距離最終位置足夠遠
if (mScrollState == SCROLL_STATE_SETTLING &&
Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) > mCloseEnough) {
//37. 如果此時用戶手指按下跳芳,則立馬暫停滑動
mScroller.abortAnimation();
mPopulatePending = false;
populate();
mIsBeingDragged = true;
//38.如果ViewPager還有父View竹勉,則還要向父View申請將觸摸事件傳遞給ViewPager
requestParentDisallowInterceptTouchEvent(true);
//39.設(shè)置當前狀態(tài)為正在拖拽
setScrollState(SCROLL_STATE_DRAGGING);
} else {
//40.結(jié)束滾動
completeScroll(false);
mIsBeingDragged = false;
}
if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY
+ " mIsBeingDragged=" + mIsBeingDragged
+ "mIsUnableToDrag=" + mIsUnableToDrag);
break;
}
case MotionEventCompat.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
break;
}
//41.添加速度追蹤
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
//42.只有在當前是拖拽切換頁面時我們才會去攔截事件
return mIsBeingDragged;
}
我們看看ViewPager
是如何決定是攔截還是不攔截飞盆,從源碼上面看出,但斜率小于0.5時次乓,則要攔截吓歇,否則不攔截,斜率是什么情況呢票腰?高中數(shù)學(xué)可知城看,在第一象限中,越靠近y軸的直線杏慰,斜率越大测柠,越靠近x軸直線斜率越小,先看簡單圖示:
也就是說缘滥,手指滑動的傾斜度比0.5小轰胁,就去攔截事件,由ViewPager
來響應(yīng)切換頁面完域。
好啦软吐,今天的學(xué)習(xí)就先到處為止啦,明天繼續(xù)研究其他部分