前段時(shí)間開的新項(xiàng)目,現(xiàn)在終于開始動(dòng)工了静汤,我和另一個(gè)小伙伴一起做居凶,由于他還在處理另一個(gè)項(xiàng)目的尾巴侠碧,所以前期只有我一個(gè)人來做。之后我也會(huì)圍繞著這個(gè)項(xiàng)目來講一些我遇到的一些問題弄兜,和聯(lián)想發(fā)散的一些問題瓷式。
動(dòng)機(jī)
“精品話題”板塊贸典,這部分我用recycleview做了個(gè)橫向滑動(dòng),然后讓女朋友試用腾夯。
問題就出在我女朋友試用后說體驗(yàn)不好:
- 滑動(dòng)速度太快了蔬充。
-
滑動(dòng)結(jié)束沒有item在中間位置。
滑動(dòng)速率太快榨呆,沒有定位庸队。
滑動(dòng)速率
找了一圈可調(diào)用的方法,卻沒有看到可以直接設(shè)置速度的竿拆。
卒宾尚!
只有從相關(guān)代碼里面找找看了。RecycleView提供了兩個(gè)滑動(dòng)監(jiān)聽:OnScrollListener和OnFlingListener
public abstract static class OnScrollListener {
//SCROLL_STATE_IDLE御板、SCROLL_STATE_DRAGGING、SCROLL_STATE_SETTLING三個(gè)狀態(tài)
public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
//滑動(dòng)過程中一直會(huì)被調(diào)用
public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
}
看了下被調(diào)用場(chǎng)景怠肋,并沒有什么卵用笙各。
public abstract static class OnFlingListener {
//Override this to handle a fling given the velocities in both x and y directions.
public abstract boolean onFling(int velocityX, int velocityY);
}
這個(gè)接口就比較有意思了,重寫onFling可以處理拋投(手指快速滑動(dòng)引起的屏幕慣性滑動(dòng))酪惭,velocityX,velocityY就是x軸春感,Y軸上的速率啊。順藤摸瓜嫩实,看看在哪設(shè)置的具體的數(shù)值窥岩。
在唯一調(diào)用onFling()的地方,我找到了這樣一段代碼:
public boolean fling(int velocityX, int velocityY) {
//其他邏輯
if (!dispatchNestedPreFling(velocityX, velocityY)) {
//其他邏輯
if (canScroll) {
//其他邏輯
velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
mViewFlinger.fling(velocityX, velocityY);
return true;
}
}
return false;
}
可見晃洒,最后先判斷能否滑動(dòng)朦乏,然后通過Math.max()確定具體數(shù)值的。
參數(shù)里的velocityX, velocityY是函數(shù)傳過來的吃引,mMaxFlingVelocity是啥肮舸浮?庐氮?
去特喵的旭愧!是個(gè)private final屬性。初始值是8000dp,最終數(shù)值根據(jù)屏幕分辨率轉(zhuǎn)換成px后確定议泵。
木有辦法,雖然不能通過set直接設(shè)置型奥,好歹也找到了屬性名,那就通過反射來做厢汹。
//設(shè)定RecyclerView最大滑動(dòng)速度
private void setMaxFlingVelocity(RecyclerView recycleview, int velocity) {
try{
Field field = recycleview.getClass().getDeclaredField("mMaxFlingVelocity");
field.setAccessible(true);
field.set(recycleview, velocity);
}catch (Exception e){
e.printStackTrace();
}
}
設(shè)置個(gè)2000,走你=缁 (設(shè)置4000使用起來比較舒服)
監(jiān)聽
定位這一步肯定是在滑動(dòng)快結(jié)束或者結(jié)束的時(shí)候經(jīng)過判斷來決定停在哪的搭综。上面寫到的兩個(gè)滑動(dòng)監(jiān)聽中,OnFlingListener 恕本人無能条获,暫時(shí)沒有想到什么辦法去使用velocityX, velocityY 兩個(gè)參數(shù)蒋歌。所以只有考慮用OnScrollListener。
onScrollStateChanged() 和 onScrolled()
onScrolled()是在視圖滾動(dòng)的過程中堂油,一直會(huì)被調(diào)用的方法∑笺玻肯定不能在這里面做判斷寓免。
onScrollStateChanged()是在滑動(dòng)狀態(tài)改變時(shí)候回調(diào)的方法。并且參數(shù)傳回來一個(gè)newState撕予。這參數(shù)代表著當(dāng)前RV的狀態(tài)。這個(gè)狀態(tài)有三個(gè)实抡。
public static final int SCROLL_STATE_IDLE = 0;
public static final int SCROLL_STATE_DRAGGING = 1;
public static final int SCROLL_STATE_SETTLING = 2;
0代表滑動(dòng)停止欢策;1代表正在被拖動(dòng);2代表當(dāng)前在慣性滑動(dòng)啄清;
當(dāng)我們拖拽view的時(shí)候有兩種情況:1→0 或者 1→2→0
所以我們就在view停止滑動(dòng)的時(shí)候再去定位就好俺孙。
定位
recyclerView.scrollBy(int x, int y);
recyclerView.scrollTo(int x, int y);
recyclerView.scrollToPosition(int position);
recyclerView.smoothScrollBy(int dx, int dy);
recyclerView.smoothScrollToPosition(int position);
嘿嘿嘿掷贾!
看到position就開心荣茫。要算寬度dp什么的最麻煩了,實(shí)在不行再去算寬度嘛港准。
現(xiàn)在就去找怎么得到當(dāng)前的position了票罐。
scrollToPosition是直接顯示position的item。
smoothScrollToPosition是平滑到position的item该押。
當(dāng)然選擇第smooth啦。
linearLayoutManager.findFirstVisibleItemPosition();
linearLayoutManager.findLastVisibleItemPosition();
linearLayoutManager.findFirstCompletelyVisibleItemPosition();
linearLayoutManager.findLastCompletelyVisibleItemPosition();
前面兩個(gè)方法是找到屏幕顯示到的第一個(gè)/最后一個(gè)item(有可能只顯示了一半)的position烟具。
后面兩個(gè)方法是找到屏幕顯示到的第一個(gè)/最后一個(gè)完整的item(有可能它兩邊還有沒顯示完整的item)的position奠蹬。
我還是太年輕了!嚶嚶嚶冀痕!
當(dāng)我們使用前兩個(gè)position時(shí)狸演,永遠(yuǎn)會(huì)遇到定位不到第一個(gè)或者最后一個(gè)的問題。
當(dāng)時(shí)候后面兩個(gè)position的時(shí)候腊尚,90%你滑出來的position因?yàn)関iew顯示不全返回-1满哪,眼睜睜看著她崩潰。而且這個(gè)smoothscroll效果也太不好了哨鸭!
上面這四種定位的方式不適合當(dāng)前情況,只適合屏幕能顯示整數(shù)個(gè)的情況勘高,也就是recycleView在最邊緣的時(shí)候,屏幕不會(huì)有顯示不全的view。從一開始的方向就錯(cuò)誤了仅乓。
點(diǎn)題,失敗的滑動(dòng)定位夸楣!
解決辦法
后來前輩給我說了這么一段代碼:
new LinearSnapHelper().attachToRecyclerView(recycleview);
用了一次之后豫喧,發(fā)現(xiàn)這玩意兒SnapHelper,真香紧显!
最后
之前都是寫設(shè)計(jì)模式,一直也沒有寫過啥有深度的話題(我認(rèn)為的有深度應(yīng)該就是會(huì)涉及到源碼的分析涉兽,或者一個(gè)很難的課題)篙程,這一次想寫有深度文章的嘗試,正如題目所說拥诡,應(yīng)該是失敗了鸣峭。唉~ 還是太弱了。本篇文章唯一有價(jià)值的信息诅岩,大概是:我有女朋友吧宾娜。