今天跟大家分享下RecyclerView怎么使它的條目自動(dòng)滾動(dòng)
其實(shí)做這個(gè)也是項(xiàng)目中有個(gè)需求需要用到,但是在網(wǎng)上找遍了也沒有找到專門講解這個(gè)知識(shí)點(diǎn)的文章。
所以這段時(shí)間自己研究懂了后 分享給大家。
再次更新下驻右,找到了一個(gè)新的解決辦法迁客,那就是 RecyclerView 的smoothScrollToPosition
方法。其實(shí)這個(gè)方法之前用過重虑,也是可以平滑滾動(dòng),但是速度太快秦士,沒辦法自定義時(shí)間缺厉。所以就沒有說 ,但是昨天晚上經(jīng)過簡(jiǎn)友的提醒隧土,我又細(xì)心的找了一遍提针,發(fā)現(xiàn)這個(gè)方法確實(shí)是可以改變時(shí)間的 而且不用像下面那么麻煩而且解決了所有問題,簡(jiǎn)直棒到?jīng)]朋友曹傀,那么下面是解決方法辐脖。
[RecyclerView調(diào)用smoothScrollToPosition() 控制滑動(dòng)速度]
一想到條目自動(dòng)滾動(dòng),讓我想起了ListView有一個(gè)方法叫做setSelection(int position)
可以設(shè)置一個(gè)位置 然后他就會(huì)自動(dòng)滾動(dòng)到指定的位置皆愉,但是現(xiàn)在這都什么年代了 果斷拋棄了LisView 使用RecyclerView來做 至于RecyclerView有什么好處 為什么使用它 大家自行Google吧嗜价。
那么我們回到RecyclerView中想想該怎么實(shí)現(xiàn)這個(gè)呢 找了找RecyclerView中方法 發(fā)現(xiàn)有這么一個(gè)方法scrollToPosition(int position)
這個(gè)方法跟ListView的一樣也是傳入進(jìn)去一位置 自動(dòng)給你跳到指定的位置 艇抠,這會(huì)兒 你可能心想 我去這不是直接就實(shí)現(xiàn)了么,但是 你用下看效果 那個(gè)條目跟瞬移是的 直接就干到最底部了炭剪! (如果你們產(chǎn)品能接受 那就當(dāng)我沒說) 既然這樣不行 那就換练链,突然想到谷歌給我們提供了一個(gè)叫做Scroller的類 看描述!
Scroller是一個(gè)專門用于處理滾動(dòng)效果的工具類奴拦,可能在大多數(shù)情況下媒鼓,我們直接使用Scroller的場(chǎng)景并不多,但是很多大家所熟知的控件在內(nèi)部都是使用Scroller來實(shí)現(xiàn)的错妖,如ViewPager绿鸣、ListView等。
不熟悉的可以看下這篇博客[Android Scroller完全解析暂氯,關(guān)于Scroller你所需知道的一切]
其實(shí)說到底他也就還是個(gè)工具類 也就是說其實(shí)最后能讓Item滾動(dòng)并不是他 這時(shí)候就要介紹下我們的另外兩個(gè)方法 scrollTo()和scrollBy()這兩個(gè)方法 這兩個(gè)才是主角 就是這個(gè)兩個(gè)方法才能讓我們的View滑動(dòng) 他們的使用也很簡(jiǎn)單 就是傳入 一個(gè)x坐標(biāo) 和一個(gè) y坐標(biāo) 他就會(huì)照著你給的位置去移動(dòng) 他倆不一樣的地方無非就是 scrollTo 是以當(dāng)前View的初始位置開始移動(dòng) 而scrollBy是根據(jù)當(dāng)前位置來進(jìn)行移動(dòng) 而其他的特性大家也可以看上面的博客 這里就不絮叨了潮模,
那么大家知道scrollTo()和scrollBy()的移動(dòng)方式 就是移動(dòng)他的內(nèi)容 而不是他自己本身,那這里正好 我們不就是想移動(dòng)RecyclerView的Item么?但是用過你會(huì)發(fā)現(xiàn)痴施,我去擎厢,這家伙怎么跟之前用的那個(gè)設(shè)置位置移動(dòng)的方法一個(gè)德行 ,也嗖的一下就干過去了 .不過他并不是移動(dòng)到底 如果是上下滑動(dòng)的話 是根據(jù)你的y值而決定的 。
那么下面就要用到Scroller這個(gè)類了
使用startScroll(int startX, int startY, int dx, int dy)
開始啟動(dòng)滑動(dòng) 并且重寫computeScroll()
方法完成值得過度 并且調(diào)用invalidate();
方法請(qǐng)求View數(shù)重新繪制 這個(gè)一定要記得調(diào)用 不要會(huì)沒有效果
@Override public void computeScroll() { //重寫computeScroll()方法辣吃,并在其內(nèi)部完成平滑滾動(dòng)的邏輯 if (mScroller.computeScrollOffset()) { scrollBy(mScroller.getCurrX(), mScroller.getCurrY()); invalidate(); } }
看下效果
使用后你會(huì)發(fā)現(xiàn)动遭,哎確實(shí)是自動(dòng)滾動(dòng)了,而且也不是嗖的一下在就沒了神得。 好像這樣確實(shí)是已經(jīng)實(shí)現(xiàn)了厘惦,但是如果我想讓他滾動(dòng)的在慢一點(diǎn)怎么辦?或者說我想他的滾動(dòng)速度是可以調(diào)的哩簿,那怎么辦 宵蕉? 其實(shí)這個(gè)startScroll 還有一個(gè)重載的方法 startScroll(int startX, int startY, int dx, int dy, int duration)
可以再添加一個(gè)時(shí)間,單位是毫秒节榜。
但是這里還有一個(gè)問題就是RecyclerView是不讓使用scrollTo方法 如果使用了是沒有效果的 而且還會(huì)打Log提醒
RecyclerView does not support scrolling to an absolute position "Use scrollToPosition instead
第二種方法
到現(xiàn)在就已經(jīng)是實(shí)現(xiàn)RecyclerView 的自動(dòng)滾動(dòng)效果了羡玛,但是有沒有其他方法可以實(shí)現(xiàn)呢?當(dāng)然有 全跨,那就是使用屬性動(dòng)畫來實(shí)現(xiàn)缝左。
屬性動(dòng)畫我們大家都知道,分為兩種重要的類浓若,分別為ValueAnimator 和 ObjectAnimator,ValueAnimator 就是來計(jì)算我們給的初始值和結(jié)束值說白了就是對(duì)值進(jìn)行計(jì)算來完成數(shù)值之間的過度動(dòng)畫 蛇数,而ObjectAnimator不僅會(huì)對(duì)值進(jìn)行計(jì)算還會(huì)通過反射的方式把計(jì)算出來的值賦值給做動(dòng)畫對(duì)象的屬性挪钓,這里我只是說個(gè)大概,但是你有沒有發(fā)現(xiàn)其實(shí)之前的Scroller也是我們給個(gè)開始位置 和結(jié)束為止 他來給我們計(jì)算數(shù)值 然后我們?cè)趕crollBy過去 你會(huì)發(fā)現(xiàn)這個(gè)計(jì)算值的過程跟上面的ValueAnimator 是不是老像了耳舅? 好 那我們就來試試 看看行不行碌上。
//默認(rèn)從0-200 valueAnimator = ValueAnimator.ofInt(200); valueAnimator.setDuration(5000); valueAnimator.setInterpolator(new LinearInterpolator());valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override publicvoidonAnimationUpdate(ValueAnimatoranimation) { //獲取估值器給我們的返回值 int animatedValue = (int) animation.getAnimatedValue(); //調(diào)用RecyclerView的scrollBy執(zhí)行滑動(dòng) recyclerView.scrollBy(0, animatedValue); Log.e("TAG", "animatedValue:" + animatedValue); } } ); valueAnimator.start();
你會(huì)發(fā)現(xiàn)效果是一樣一樣的 倚评,但是實(shí)驗(yàn)到這里 我想悲哀的告訴大家,這里面有個(gè)Bug馏予,細(xì)心的可能發(fā)現(xiàn)了天梧,當(dāng)我們不論是用上面那種方法,當(dāng)你的Item內(nèi)容比較少霞丧,而我們的結(jié)束值又是隨便設(shè)置的呢岗,執(zhí)行時(shí)間稍微長(zhǎng)一點(diǎn) 你就會(huì)發(fā)現(xiàn),哎蛹尝?我怎么不能往上面滑動(dòng)了后豫,但等一會(huì)又可以滑動(dòng)了,其實(shí)是這個(gè)scrollBy還沒有執(zhí)行完突那, 還在往下滑動(dòng)挫酿,只不過是滑動(dòng)到頭了,你看不見罷了 愕难,那又怎么決解這個(gè)問題呢 早龟?其實(shí)RecyclerView有一個(gè)監(jiān)聽 addOnScrollListener 如果你沒有這個(gè)方法 說明你該更新了RecyclerView了 這個(gè)監(jiān)聽有兩個(gè)回掉。
- onScrollStateChanged 滑動(dòng)狀態(tài) 兩個(gè)參數(shù)一個(gè)View 一個(gè)狀態(tài) 分為按下滑動(dòng) 和抬起 和MotionEvent 是一樣的 猫缭。
- onScrolled 三個(gè)參數(shù) View 和 x,y軸滑動(dòng)的量葱弟。
其實(shí)通過這兩個(gè)回掉很簡(jiǎn)單的就能解決這個(gè)問題 就是我們只要能判斷出當(dāng)前這個(gè)RecyclerView 是不是滑動(dòng)到底部就行了 在onScrollStateChanged中判斷? 當(dāng)滑動(dòng)的狀態(tài)為抬起的時(shí)候我們判斷 不行 因?yàn)槲覀兪鞘褂胹crollBy方法進(jìn)行的滑動(dòng) 妹的 這個(gè)方法根本就不會(huì)執(zhí)行 他只有你手指滑動(dòng)的時(shí)候才會(huì)執(zhí)行饵骨,那咋辦 只有在onScrolled 中判斷了 這個(gè)方法才不會(huì)管你什么滑動(dòng)呢 只要我動(dòng)了 他就執(zhí)行 那怎判斷呢翘悉?
(直接圖片了 代碼這個(gè)格式真心沒弄好 怎么弄都不行 !)
大概的過程就是 居触,我開始執(zhí)行動(dòng)畫 妖混,然后不斷判斷是否達(dá)到最底部 ,不是得話就繼續(xù)執(zhí)行滑動(dòng)轮洋。這時(shí)候呢制市,因?yàn)槲冶O(jiān)聽了RecyclerVIew的滑動(dòng) 一滑動(dòng)我就判斷。
怎么判斷呢弊予?得到當(dāng)前顯示的最后一個(gè)item的view祥楣,通過這個(gè)View 得到他的bottom坐標(biāo)值,然后在獲得RecyclerView的bottom坐標(biāo)值汉柒,在拿到最后View的position误褪,在拿到RecyclerView item的總數(shù)-1,他們四個(gè)進(jìn)行比較碾褂。 都滿足了兽间,就能說明,當(dāng)前已經(jīng)滑動(dòng)到最底部了正塌,這時(shí)候給isBottom賦值為True 嘀略,接著動(dòng)畫監(jiān)聽里面我們之前不是做了判斷么恤溶!滿足else條件 取消動(dòng)畫,就不會(huì)在執(zhí)行了帜羊,那個(gè)Bug也就解決了咒程。
但是現(xiàn)在這個(gè)方法還有效率問題,滑動(dòng)就執(zhí)行判斷讼育,而且還是獲取View的各種信息帐姻,想辦法在優(yōu)化吧。還有一個(gè)就是窥淆,如果我們能知道具體移動(dòng)到什么位置就好了 ,我們上面設(shè)置的是200,因?yàn)檫@個(gè)參數(shù)是坐標(biāo),而RecyclerView的內(nèi)容其實(shí)已經(jīng)超過屏幕坐標(biāo)系了,這個(gè)問題待解決卖宠。。忧饭。扛伍。
好了 以上就是我總結(jié)的一些開發(fā)中的經(jīng)驗(yàn) 如果有誰能知道解決這個(gè)辦法 和寫的不對(duì)的地方 歡迎指教 。词裤。刺洒。