5年了,ViewPager2 终于支持 overScrollMode,没错,我干的。
The following article is from Android补给站 Author Mr_万能胶
本文作者
作者:Mr_万能胶
链接:
https://juejin.cn/post/7394463308379045951
本文由作者授权发布。
注意标题中的我,指的是本文作者Mr_万能胶,大家可以去掘金瞻仰。
这两周给 androidx 做了一点微小的贡献,可算是把多年来的一个小坑给填上了,今天有时间就写一篇文章,详细记录一下整个过程。
<androidx.viewpager.widget.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"/>
而当你迁移到 ViewPager2 之后,如果同样的方式设置这个属性,你会发现这个属性失效了。有同学会怀疑可能是 xml 初始化的问题,于是跑去代码里再设置一次,会发现同样无效。
彼时 androidx 还在 github 积极开发,有开发者发现了这个问题,先提了 issue(至今还是 Open 状态),而后过了大半年没人管,大家觉得可能这样还不够引起重视,于是有人直接提到了 Issue tacker,这个 Google 内部拿来跟踪 bug 的。
https://github.com/material-components/material-components-android/issues/459
https://issuetracker.google.com/issues?q=158234055
然后一恍就是5年,5年了,没人管。
<androidx.viewpager.widget.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_pare再@NonNull
public EdgeEffect mLeftEdge;
@NonNull
public EdgeEffect mRightEdge;
@Override
public void draw(@NonNull Canvas canvas) {
super.draw(canvas);
boolean needsInvalidate = false;
final int overScrollMode = getOverScrollMode();
if (overScrollMode == View.OVER_SCROLL_ALWAYS
|| (overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS
&& mAdapter != null && mAdapter.getCount() > 1)) {
if (!mLeftEdge.isFinished()) {
// ...
}
if (!mRightEdge.isFinished()) {
// ...
}
} else {
mLeftEdge.finish();
mRightEdge.finish();
}
nt"
android:layout_height="match_parent"
android:overScrollMode="never"/>
// Based on movement, we may want to trigger the hiding of existing over scroll
// glows.
if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
considerReleasingGlowsOnScroll(unconsumedX, unconsumedY);
}
View child = viewPager2.getChildAt(0);
if (child instanceof RecyclerView) {
child.setOverScrollMode(View.OVER_SCROLL_NEVER);
}
fun ViewPager2.setOverScrollModeExt(overScrollMode: Int) {
val view = getChildAt(0)
if (view is RecyclerView) {
(view as RecyclerView).overScrollMode = overScrollMode
}
}
“ViewPager2 的第一个子 View 一定是 RecyclerView”
如果哪天 Google 换了设计或者改了方案,在 RecyclerView 外面再套一层,这个方法就会失效。
来到今年下半年,由于项目的关系又用到了这里,我实在看不下去了,翻出来前面所有这些 bug,给 Google 提了一个 CL(Google 把每一个 Gerrit 上的提交称为 CL,即 Change Line):
ViewPager2 在初始化的时候,维护了一个 initialize() 方法,在这个方法里去初始化了 RecyclerView,并将其 add 到了自己的 ViewGroup,因此,我们需要在这一步开始就关心一下 overScrollMode,并且透传给 RecyclerView 设下去。 ViewPager2 必须重写 setOverScrollMode(int overScrollMode) 方法,这确保了开发者在手动在代码里调方法设置的时候也能生效。不要忘记 super.setOverScrollMode(overScrollMode);,这确保了你不用自己维护 android.view.View#mOverScrollMode,从而能确保 android.view.View#getOverScrollMode() 的返回值正确。
在大约半年前,我写了一篇关于单元测试的文章,向大家详细介绍了单元测试在 Google Android 项目中的重要性,如果你有兴趣,可以再次阅读:
https://juejin.cn/post/7323399314549145600
确保开发者从 xml 初始化,和从代码初始化 ViewPager2 的时候,设置的 overScrollMode 能被正确读取,且设置下去。 确保 ViewPager2 的 overScrollMode 与内部的 RecyclerView2 的 overScrollMode 保持同步,这样就能确保设置是生效的。
大块的代码就不贴了,如果大家有兴趣,可以直接这里阅读。
从时间线上可以看出,只要代码质量过硬,符合贡献标准,其实 Google 的 androidx 团队成员还是很乐于跟进的,我在周五下班前提交了代码,经过了一系列 review、CI,和一个愉快的周末,这笔提交已经在周二 Merge。按照以往的节奏,大概率在3个月之后的 androidx 新版本里面就可以体现。
最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!
推荐阅读:
扫一扫 关注我的公众号
如果你想要跟大家分享你的文章,欢迎投稿~
┏(^0^)┛明天见!