Android做應(yīng)用不免需要使用到列表窍仰,說到列表岩四,大部分人會(huì)想到ListView恭朗,當(dāng)然現(xiàn)在也有很多人會(huì)想到RecyclerView屏镊,因?yàn)楫吘故枪雀韫俜酵扑]嘛,什么定制性更高痰腮,功能更強(qiáng)而芥,更高效。
實(shí)話說膀值,ListView對(duì)于大部分人來說棍丐,足夠用了,因?yàn)楫吘故煜ecyclerView需要時(shí)間沧踏,RecyclerView也有一些不大不小的坑歌逢。不管怎么說,我在項(xiàng)目中用上了RecyclerView翘狱,本沒什么秘案,跟ListView一樣,現(xiàn)主要介紹下這幾天使用RecyclerView碰到的事潦匈,也希望其他人少走彎路阱高。
情況是這樣子的,我們需要對(duì)頁面中整個(gè)數(shù)據(jù)緩存历等,并記錄用戶瀏覽位置讨惩,下次用戶再次進(jìn)入當(dāng)前頁面時(shí)辟癌,能定位到之前的瀏覽位置寒屯,很好的用戶體驗(yàn),開始做...
關(guān)于數(shù)據(jù)緩存黍少,我們是使用Realm存儲(chǔ)的寡夹,Realm確實(shí)神器啊,傻瓜式操作厂置,增刪減查菩掏,幾句代碼搞定,不用像android原生數(shù)據(jù)庫(kù)一樣昵济,所有的都要自己實(shí)現(xiàn)智绸。一開始的邏輯是,在退出頁面時(shí)访忿,使用Realm記錄當(dāng)前頁面所有數(shù)據(jù)瞧栗,并記錄用戶滑動(dòng)的位置;當(dāng)用戶重新進(jìn)入頁面時(shí)海铆,讀取數(shù)據(jù)庫(kù)數(shù)據(jù)迹恐,加載到RecyclerView中,然后滑動(dòng)到上次瀏覽位置卧斟。
當(dāng)數(shù)據(jù)少時(shí)殴边,這樣做確實(shí)沒問題憎茂,上面的操作基本在幾十毫秒內(nèi)解決,但是一旦數(shù)據(jù)上百或幾百條時(shí)锤岸,Realm讀取數(shù)據(jù)時(shí)間明顯增加竖幔,RecylcerView加載數(shù)據(jù)時(shí)間也相應(yīng)增加,最關(guān)鍵的是滑動(dòng)到上次瀏覽位置時(shí)是偷,能明顯看到滑動(dòng)動(dòng)畫赏枚,當(dāng)然我用的滑動(dòng)方法時(shí)下面這個(gè)
recyclerView.post(new Runnable() {
@Override
public void run() {
recyclerView.scrollBy(0, 100);
}
});
事后證明我用的這個(gè)滑動(dòng)方法也有問題,當(dāng)然這是后話晓猛。
實(shí)際證明饿幅,這種實(shí)現(xiàn)緩存并滑動(dòng)到瀏覽位置的方案行不通。然后一通的谷歌戒职,百度栗恩,然后又是看到一大堆博客類似上面代碼方式,很無奈...
這時(shí)候洪燥,IOS大哥過來跟我說磕秤,它們可以存儲(chǔ)整個(gè)頁面,然后再次進(jìn)頁面的時(shí)候只要重新加載這個(gè)存儲(chǔ)的頁面就好捧韵,是不是so easy市咆,沒有數(shù)據(jù)庫(kù)緩存,沒有RecyclerView滑動(dòng)這回事再来;我就估摸著蒙兰,Android是不是可以存儲(chǔ)整個(gè)Activity呢?想到就做芒篷,確實(shí)可以保存整個(gè)Activity搜变,但是怎么加入到當(dāng)前的Activity棧中呢?開始查資料针炉,繼續(xù)一通谷歌百度挠他,然而好像還是不知道怎么加(有知道的哥們希望告訴下),倒是額外學(xué)到了一個(gè)很有用的方法moveTaskToBack()篡帕,算是浪費(fèi)時(shí)間的補(bǔ)償吧殖侵。
又一條路不通,開始意識(shí)到是不是陷入死胡同了镰烧,有些煩了拢军,刷知乎吧,本著鳥槍法的原則拌滋,不經(jīng)意的在知乎也搜了關(guān)于這個(gè)問題的方案朴沿,看了十幾篇好像也沒什么用,快失望的時(shí)候,突然看到一句評(píng)論:緩存Fragment赌渣∥呵Γ看到這句評(píng)論的時(shí)候,真有種熱淚盈眶的感覺坚芜,是啊览芳,緩存整個(gè)Activity行不通,緩存Fragment總行吧鸿竖,想到就做沧竟,每當(dāng)用戶退出當(dāng)前頁面時(shí),在內(nèi)存中緩存當(dāng)前Fragment缚忧,下次進(jìn)這個(gè)頁面時(shí)悟泵,直接加載這個(gè)Fragment,實(shí)驗(yàn)很成功闪水,進(jìn)入頁面直接就在上次瀏覽位置糕非,不會(huì)出現(xiàn)滑動(dòng)動(dòng)畫,完美實(shí)現(xiàn)需要的效果(當(dāng)然加載這個(gè)緩存的Fragment時(shí)球榆,里面涉及到Activity的對(duì)象全部需要重新加載)朽肥。
按理說,事情到這里應(yīng)該就完了持钉。但是不對(duì)衡招,緩存的Fragment帶有Activity的引用(雖然這個(gè)Activity已經(jīng)在退出時(shí)被銷毀了),不會(huì)釋放每强,會(huì)造成內(nèi)存泄露始腾,出了問題就解唄,然而研究一通發(fā)現(xiàn)舀射,要緩存整個(gè)Fragment窘茁,這個(gè)內(nèi)存泄露的問題必定會(huì)出現(xiàn)怀伦,最后也未發(fā)現(xiàn)解決的方法脆烟,就暫時(shí)擱置了。
雖然擱置這件事房待,但內(nèi)心總有心疙瘩邢羔,瀏覽技術(shù)網(wǎng)站時(shí),總是時(shí)不時(shí)的會(huì)搜索關(guān)于這個(gè)問題的解決方法桑孩,無意中發(fā)現(xiàn)ListView存在一個(gè)方法setSelectionFromTop可以直接直接回到瀏覽位置拜鹤;既然ListView有這個(gè)方法,RecyclerView應(yīng)該也有吧流椒?然而查詢之后敏簿,發(fā)現(xiàn)那只是我的意淫;不過,沒這個(gè)方法惯裕,也并沒有放棄温数,RecyclerView既然是可以替代ListView的,總覺得是有和setSelectionFromTop相同功能的方法的蜻势,最后也確實(shí)找出了撑刺,scrollToPositionWithOffset,嗯握玛,好像不再RecyclerView里面够傍,去LinearLayoutManager里面試試,終于找到挠铲!
它有2個(gè)參數(shù)冕屯,第一個(gè)參數(shù)代表你要滑動(dòng)到哪個(gè)Item,第二個(gè)參數(shù)代表這個(gè)item距離屏幕頂部的距離拂苹,這個(gè)距離可以使用下面代碼計(jì)算得出:
int position = linearLayoutManager.findFirstVisibleItemPosition();
View view = recyclerView.getChildAt(position);
if (view != null) {
int top = view.getTop();
}
ok愕撰,實(shí)現(xiàn)了效果,不卡頓醋寝,也不會(huì)有滑動(dòng)動(dòng)畫搞挣,也不會(huì)出現(xiàn)內(nèi)存泄露,問題解決了嘛音羞?
這次是徹底解決了囱桨,以上記錄苦逼的繞坑過程,那么多的研究嗅绰,發(fā)現(xiàn)最后一個(gè)scrollToPositionWithOffset就解決了舍肠,是不是很崩潰?我確實(shí)崩潰了窘面。
最后翠语,希望技術(shù)博客的轉(zhuǎn)載哥哥們,轉(zhuǎn)載之前自己試試效果吧财边,因?yàn)橛袝r(shí)候真的挺坑人的肌括!哭!