ViewPager2简介
距离ViewPager2
正式版的发布已经一年多了,目前ViewPager
早已停止更新,官方鼓励使用ViewPager2替代。 ViewPager2
底层基于RecyclerView
实现,因此可以获得RecyclerView
带来的诸多收益:
- 抛弃传统的
PagerAdapter
,统一了Adapter
的API
; - 横向、竖向布局都可以实现自由滑动;
- 支持
DiffUitl
,可以实现局部刷新; - 支持
RTL
(right-to-left),对于一些有出海需求的APP非常有用; - 支持
ItemDecorator
,搭配PageTransformer
实现炫酷的跳转动画;
ViewPager2
更多的是配合Fragment
的使用,这需要借助于FragmentStateAdapter
。
他们偶尔会搭配TabLayout
一起使用,相关代码直接阅读或者运行 ViewPager2官网Samples 即可,这里不做重复的讲解。
下面主要讲一下在使用过程中遇到的问题~!
实际操作效果
上滑吸顶+标题页面左右滑动+横滑和竖滑列表+标题页面数据和数量更新
上滑吸顶
CoordinatorLayout
+AppBarLayout
+CollapsingToolbarLayout
左右滑动
ViewPager2
+TabLayout
+Fragment
横滑和竖滑列表
RecycleView
+NestedScrollableHost
标题页面数据和数量
TabLayoutMediator
+声明周期检测+缓存优化
RecycleView和Viewpage2的滑动冲突
/**
- Created by Tanzhenxing
- Date: 2021/4/7 7:04 下午
- Description:解决 [RecyclerView] 嵌套到 [androidx.viewpager2.widget.ViewPager2] 左右滑动冲突
- 目前只解决了左右滑动冲突
*/
class RecyclerViewAtViewPager2 : RecyclerView {
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
var x1 = 0f
var x2 = 0f
override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
if(event!!.action == MotionEvent.ACTION_DOWN) {
x1 = event.x
} else if(event.action == MotionEvent.ACTION_MOVE) {
x2 = event.x
} else if (event.action == MotionEvent.ACTION_CANCEL
|| event.action == MotionEvent.ACTION_UP) {
x2 = 0f
x1 = 0f
}
val xOffset= x2-x1
if (layoutManager is LinearLayoutManager) {
val linearLayoutManager = layoutManager as LinearLayoutManager
if (linearLayoutManager.orientation == HORIZONTAL) {
if ((xOffset <= 0 && canScrollHorizontally(1))
|| (xOffset >= 0 && canScrollHorizontally(-1))) {
this.parent?.requestDisallowInterceptTouchEvent(true)
} else {
this.parent?.requestDisallowInterceptTouchEvent(false)
}
} else {
// TODO: 2021/4/8 目前没有实现上下滑动和 [androidx.viewpager2.widget.ViewPager2]上下滑动的冲突
}
} else {
handleDefaultScroll()
}
return super.dispatchTouchEvent(event)
}
fun handleDefaultScroll() {
val canScrollHorizontally = canScrollHorizontally(-1) || canScrollHorizontally(1)
val canScrollVertically = canScrollVertically(-1) || canScrollVertically(1)
if (canScrollHorizontally || canScrollVertically) {
this.parent?.requestDisallowInterceptTouchEvent(true)
} else {
this.parent?.requestDisallowInterceptTouchEvent(false)
}
}
}
ViewPager2中Fragment的懒加载
懒加载
一般我们使用Fragment
对页面进行数据懒加载的时候都是通过onHiddenChanged
方法判断显示和隐藏,在第一次展现出来的时候再进行接口调用。
@Override
public final void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if (!hidden) {
onUserVisible();
} else {
onUserGone();
}
}
但在ViewPager2
中,Fragment
的setUserVisibleHint
和onHiddenChanged
方法都是不执行的。
ViewPager
展现第一个页面,然后切后台的日志:
04-17 16:45:10.992 D/tanzhenxing:11(22006): onCreate
04-17 16:45:10.992 D/tanzhenxing:11(22006): onCreateView:
04-17 16:45:11.004 D/tanzhenxing:11(22006): onActivityCreated
04-17 16:45:11.004 D/tanzhenxing:11(22006): onViewStateRestored: 184310198
04-17 16:45:11.004 D/tanzhenxing:11(22006): onStart
04-17 16:45:11.004 D/tanzhenxing:11(22006): onResume
04-17 16:45:18.739 D/tanzhenxing:11(22006): onPause
04-17 16:45:18.779 D/tanzhenxing:11(22006): onStop
然后在切回前台的日志:
04-17 16:53:40.749 D/tanzhenxing:11(22006): onStart
04-17 16:53:40.752 D/tanzhenxing:11(22006): onResume
ViewPager
展现第一个页面,然后手动滑动到第二个页面日志:
04-17 16:54:44.168 D/tanzhenxing:12(22006): onCreate
04-17 16:54:44.168 D/tanzhenxing:12(22006): onCreateView:
04-17 16:54:44.178 D/tanzhenxing:12(22006): onActivityCreated
04-17 16:54:44.178 D/tanzhenxing:12(22006): onViewStateRestored: 47009644
04-17 16:54:44.178 D/tanzhenxing:12(22006): onStart
04-17 16:54:44.553 D/tanzhenxing:11(22006): onPause
04-17 16:54:44.554 D/tanzhenxing:12(22006): onResume
这样看起来我们是可以利用Fragment
声明周期中的onStart
和onResume
方法做懒加载。
预加载
只要讲数据请求写在 onCreateView
或者onStart
中就能进行接口的离屏请求。
FragmentStateAdapter
ViewPager2
继承自RecyclerView
,大概率FragmentStateAdapter
继承自RecyclerView.Adapter
:
public abstract class FragmentStateAdapter extends
RecyclerView.Adapter implements StatefulAdapter {
}
onCreateViewHolder
onCreateViewHolder
是RecycleVeiw
用于创建ViewHolder
的方法:
@NonNull
@Override
public final FragmentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
return FragmentViewHolder.create(parent);
}
FragmentViewHolder
的主要作用是通过FrameLayout
为Fragment
提供用作容器的container
:
@NonNull
static FragmentViewHolder create(@NonNull
ViewGroup parent) {
FrameLayout container = new FrameLayout(parent.getContext());
container.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
container.setId(ViewCompat.generateViewId());
container.setSaveEnabled(false);
return new FragmentViewHolder(container);
}
onBindViewHolder
onBindViewHolder
是RecycleVeiw
用于数据绑定的方法:
@Override
public final void onBindViewHolder(final @NonNull FragmentViewHolder holder, int position) {
/**部分代码省略 */
ensureFragment(position);
/**部分代码省略 */
gcFragments();
}
ensureFragment(position)
,其内部会最终回调用createFragment
创建当前Fragment
。
private void ensureFragment(int position) {
long itemId = getItemId(position);
if (!mFragments.containsKey(itemId)) {
// TODO(133419201): check if a Fragment provided here is a new Fragment
Fragment newFragment = createFragment(position);
newFragment.setInitialSavedState(mSavedStates.get(itemId));
mFragments.put(itemId, newFragment);
}
}
mFragments
缓存创建的Fragment
,供后面placeFramentInViewholder
使用; gcFragments
回收已经不再使用的的Fragment
(对应的item已经删除),节省内存开销。
onViewAttachedToWindow
onViewAttachedToWindow
是ViewHolder
出现在页面中回调。
@Override
public final void onViewAttachedToWindow(@NonNull final FragmentViewHolder holder) {
//将FragmentViewHolder的container与当前Fragment绑定
placeFragmentInViewHolder(holder);
gcFragments();
}
FragmentStateAdapter使用
Fragment
对象容器;- 生产
fragment
识别的id
;
class MyFragmentStateAdapter(val data: List, fragment: Fragment) : FragmentStateAdapter(fragment){
private val fragments = mutableMapOf<Int, Fragment>()
override fun createFragment(position: Int): Fragment {
val value = data[position]
val fragment = fragments[value]
if (fragment != null) {
return fragment
}
val cardFragment =
NestedAllRecyclerViewFragment.create(value)
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://i-blog.csdnimg.cn/blog_migrate/c850a61428a67052b1a6c00501d7f7de.jpeg)
最后我还整理了很多Android中高级的PDF技术文档。以及一些大厂面试真题解析文档。
Android高级架构师之路很漫长,一起共勉吧!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
![](https://i-blog.csdnimg.cn/blog_migrate/c850a61428a67052b1a6c00501d7f7de.jpeg)
最后我还整理了很多Android中高级的PDF技术文档。以及一些大厂面试真题解析文档。
[外链图片转存中…(img-YNwprCsS-1712301366836)]
Android高级架构师之路很漫长,一起共勉吧!