【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置


被我忽视的更新

androidx recyclerview 1.2.0-alpha02 版别增加了新功用 MergeAdapter,协助开发者更容易地为 RecyclerView 增加 Header 和 Footer。详情拜s ] P g $ 1 – u n见 【译】MergeAdapter 的运用 运用官方 API 为 Recyclerview 增加 Header 和 Footer

该版别中还有一个改动:RecyclerView.Adapter lazy state restoration,协助开发者康复 RecyclerView 的状况

【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置
recyclerview update

我对这个功用并没有什么感觉。众所周知,Android 中的 View 内部是有着状况保存和康复的P x . ~ k + – v H办法的。RecyclerVie} o s p U @ jw 也是如此,它能够康复自身已翻滚的方位

【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置
View 内部康复状况

有关状况保存的内容能够拜见 【背上Jetpack】~ i 1 a 7 t k _绝不丢掉的状况 androidx SaveState ViewMode_ F =l-SaveState 剖析

真实情况也是如] z c ^ nM M T 2 t i c

【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置
RecyclerView 内部能够康复翻滚方位

意外发现

最近看到 Florina Muntenescu 的 Restore RecyclerView scroll position ,其间介绍了 RecyclerView.Adapter lazy state restoration,这勾起了我的爱好

【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置
意外发现

如文中描述,RecyclerView 在 activitg 2 9 q u Ny/fragment 重建时失掉翻滚方位是因为 Adapter 中的数据是 异步 加载的,当 RecyclW 9 I @ rerView layout 时数据并没有加载,因此也康复不了之前的方位状况。一个比较简单的比如是运用 NaP n ( # K Q 7vigation 组件进行导航,返回时 fragment 中的 RecyclerView 因为再次调用接口获取数据,导$ C _ ] H * O 5 S致其滑动方位失掉

【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置
推迟加载数据,无法康复翻滚方位

解决计划

有几种办法能够确保 RecyclerView 康复到正确的翻滚方位,最好的办法是凭借u D & E缓存,ViewModel 或 Repositorj : } + 2 [ k y 中缓存要显示l Y O的数据,确保一直在z P s 9 2 S R第一个布局传入前在 Adapter 上设置数据。也有一些其他的计划,这些计划要么太复杂,要么不够优雅

recyclerview:1.2.0-alpha02 中的解决计划是供给一个新的 Adapter 办法,该办法允许设置状况康复战略,它有三个选项

  • ALLOW
  • PREVENT_WHEN_EMPTY
  • PREVENz G . T R KT

ALLOW

这是 默许 的状况,它会当即康复 RecyclerView 的状况,该种战略无法解决} ) 5 j g推迟加载的数据的问题,能够运用 PREVENT_WHEN_EMPTY

PREVENT_WHEN_EMPTU p l O q | pY

仅当 Adapter 不为空(adapter.getItemCount() > 0)时,才康复 RecF G Hycl` u _ g @ 9 7 5 oerView 状况。 假如您的数据是异步加载的,那么 RecyclerView 会一直等到数据加载完毕,然后状况才能康复。 假如您有默许 item(例如 Header 或 加载指示器)作为适配器的一部分,则应该运用PREVENT 选项,除非运用 Merge: f 1 2 % ^ nAdapter 增加了默许 item。 MergeAdapter 等候所有适配器准备就绪,然后才康复状况

PREV= O hENT

状况不会康复,直到装备了p ~ n ALLOW 或者 PREVENT_WHEN_EMPTY

运用办法如下:

adapteY ; x q b E br.stateRestorationPolicy = PREVENT_WHEN_EMPTY

参加了上面的装备后即便是异步加载数据也能康复 RecyclerVieu * N _ 8w 的方位l t { 5 $ _ j E

【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置
设置 PREVENT_WHEN_EMPTY

追踪引进过程

老规矩,咱们沿@ & ) u 着官方的 commit log 来看看其完成原理

首要咱Q Q W ! 0 F p们看看 IssueTracker 上提M M i F n的 Feature

【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置
IssueTracker

表达的意思也很简单,便是当加载异步数据时 RecyclerView 的方位状况9 F 0 F k无法康复,Adapter 应该/ D供给相关的解决计划

有意思的是,完成该功用时还从头完成了前一个版别的逻辑,我在 git commit log 中看到了 rever? K k vt 操作

【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置
revert操作
【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置

为了防止 LayoutManager#onRestore 履行多次,没有采用最开n y ! e e Y E q端的完I p W M %成办法。但s w T c i L | + Yigit Boyar (这个提交的开发者) 仍然期望运用最开端的完成办法,但是 LayoutManager#onRestoreInstance 的状6 * 0 A l况时h 2 | = publO N f K G /ic ,因此只能选取一个折中的计划

【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置
新的完成计划
【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置
无法之举

曩昔,开发者会无意间调q r }onRestoreInstanceh f wState(State)z 3 $ ] [ Q8 : 4 x q 3法。( j U % @ U J例如,一些S u 9 *开发者已运用它来手动设置自己更新的状况,这样即便在此状况之前已康复,在此处传递状况也将导致 LayoutManager 接收它并相应地更新其8 t A内部状况。因此,即便看起来如同很奇怪$ k N o,也有必要一直调用 requestL5 M t { 2 5 3 [ Mayout 来保存功用

源码剖析

接下来咱们来剖析这部E ^ u x Y k ^ (分源码,内容很少,所以咱们具体F u o看下

首要是引进 StateRestorationPolicy的枚举l & 4 = 5 J

【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置

然后需要供给= P $ f sP N , r V h ietStateRestorJ 0 5 G + B iationPolicygetStateRestorationPoD M = & clicy 办法,此时咱们还需要一个办法来判断是否要将 SavedState 传递给 LayoutManager

【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置

前面的 setStateRestorationPo: v . I Tlich T W a My 办法中 调用了 nN m B i a o y 9 otifyStateRestorationPolicyChanged,而 notifyStateReU p S ( I - e L ~storationPolicyChanged 为静态类 Adapter4 7 H f & ; GDataObservab P D ` @ ] O 4le 中的办法,该类中的其他办法咱们也很熟悉,均是刷新 Adapter 中数据的办法。

【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置
notifyStateRestorationPolicyChE L c v i [ |anged

notifyStateRest) 5 Q e : (orationPolicyChanged 中调用了 mObservers listu ( O ; Q v 0 o q 中元素的 onStateRestorationPolicyChanged 办法,通过源码咱们得知该 list 中的元素类型为 AdapterDataObserver,因此还需要在 AdapterDataObserver 中参加 onStateRestoratij : qonPolY i ?icyChanged 办法

【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置
onStateRestorationPolicyChanged

该办法是个空完成,而 RecyclerViewDataObserver 重写了该n H 7 C [办法

【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置
Recyu & ~ $ VclerViewDataObserver

装备康复战略以及康复战略变化时的监听都有了,接下来要O [ f ~ B k U做的便是假如之前x ) 8 0 c _有待康复的装则康复之A . 7 W R 3 F l :前的状况

【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置
康复状况

注意:发布之前 StateRestorationPolicy 叫做 StateRes? 7 6 5 & B F ctorationStrategy,后来命名为 StateRestorationPolicy,alpha 版别的z R v _ k , 4 6库或许随时更改 API 的命名和删去 API,因此检查这部分源码的同学请注意

至此,相关的源码都在这儿了

总结

Statd R 3 + keRestorationPolicy 供给了 RecyclerView 异步加载数据康复翻滚方位的解决计划。原理便是通过装备 StateRestorationPolicy 来改动康复战略,同时在战略改动时调用 requestLayout 办法。. 5 ;dispatchLayoutStep2() (该办法会在 onLayou^ ~ * 2 S –t 和 measurej B N ) , L ! 办法中调用) 办法中康复状况K b P M [ h @ L(假如 cay } UnRestoreState() 返回 true)

demo 地址

一点思考:咱们都知道 VieM [ ? : #wPager2 是运用 RecyclerView 完成的,那么凭借本文介绍的 API 能够做点什. f w c C Y % E么吗?

欢迎各位小伙伴在谈论区留言,说说你的想法

关于我

我是 Fly_with24

  • 掘金

  • 简书

  • Github

【Jetpack更新之Recyclerview】更优雅地恢复 recyclerview 的滚动位置

发表评论

提供最优质的资源集合

立即查看 了解详情