一、前言
带有列表的页面是App中最常见的页面之一,这些页面有着高度相似的地方。比如一般都会有刷新功能、需要分页加载、需要处理数据异常情况、没内容时要展示空页面等等。开发者一般都会将功能类似,重复使用的功能封装起来,很多人的做法是创建BaseListActivity/Fragment。这本没啥问题,但考虑一种情况,当项目中有几十个列表页面,突然有一天需要对BaseList功能进行更改,他的子类也必须要变动,那么对于更改这几十个子页面来说无疑是令人崩溃的,这就是继承带来的副作用——高耦合。优化方案可参考这篇文章,这里我们探讨如何不使用继承简单高效封装列表功能。
二、页面实现
假设需要实现这样一个页面,我们先实现在讲细节吧。
1. 开撸之前先对页面构成简要分析:
-
- 顶部是由ViewPager2实现的自动轮播Banner。
-
- 可左右切换页面的ViewPager2。
-
- 带有刷新和分页功能的标准列表子页面。
2.上代码(这里只展示列表UI相关的完整代码,数据获取部分可看demo)
-
- xml编写
// Banner
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="200dp"
// 自动滚动
app:auto_scroll="@{true}"
// 添加item样式
app:itemBinderName='@{"com.rongc.wan.ui.WanBannerBinder"}'
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
// 自动轮播
app:loop="@{true}"
// 绑定数据
app:items="@{viewModel.banners.data}"
// 滚动间隔
app:scroll_interval="@{5000}" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabStrip"
style="@style/MyTablayoutstyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="scrollable"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/viewPager" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pagerList"
android:layout_width="match_parent"
android:layout_height="0dp"
app:items="@{viewModel.tabs}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/tabStrip" />
复制代码
-
- 页面编写(顶部的banner在xml中就已经完成了,这里仅需关注可切换ViewPager2的实现)
class WanHomeFragment : BaseFragment<FragmentWanHomeBinding, WanHomeViewModel>(), IPagerHost {
/**
* 返回需要PagerAbility的ViewPager
*/
override val viewPager: ViewPager2 get() = mBinding.pagerList
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// 注册PagerAbility
registerAbility(PagerAbility(viewModel, this))
// 分类数据请求结果订阅,ignoreLoading()为忽略加载中状态只关心结果
// whenSuccess,只在请求成功后接收通知
viewModel.result.whenSuccess(lifecycleOwner) {
// 配置TabLayout
it.data?.forEach { project ->
val tab = mBinding.tabStrip.newTab()
tab.text = project.name
mBinding.tabStrip.addTab(tab)
}
TabLayoutMediator(
mBinding.tabStrip, mBinding.pagerList, true, true
) { tab, position ->
tab.text = it.data?.getOrNull(position)?.name ?: ""
}.attach()
}
}
/**
* 若ViewPager2内容非View(是Fragment)时重载此方法返回BaseFragmentPagerAdapter
* 否则不需要提供Adapter
*/
override fun providerAdapter(): RecyclerView.Adapter<*> {
return object : BaseFragmentPagerAdapter<String>(this) {
override fun createItemFragment(item: String, position: Int): IPagerItem<String> {
// 根据position返回子页面
return ProjectListFragment().apply {
arguments = bundleOf("cid" to item)
}
}
}
}
/**
* BaseFragmentPagerAdapter照样支持空页面
*/
override fun setupEmptyView(builder: EmptyBuilder) {
builder.whenDataIsEmpty {
tip = "no data found"
}
}
}
复制代码
- 3.子列表页面Fragment
class ProjectListFragment : BaseFragment<BaseRecyclerWithRefreshBinding, ProjectListViewModel>(),
IPagerItem<String>, IRecyclerHost {
override val recyclerView: RecyclerView get() = mBinding.recyclerView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewModel.cid.value = arguments?.getString("cid")
// 注册ListAbility实现列表相关功能
registerAbility(ListAbility(viewModel, this))
}
override fun registerItemBinders(binders: ArrayList<BaseRecyclerItemBinder<out Any>>) {
// 添加列表Item样式Binder
// 有几种ItemType就添加几个对应的ItemBinder
binders.add(ProjectItemBinder())
}
}
复制代码
- 4.列表Item xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<!-- 如定义了名称为‘bean’的属性,Binder会自动为它赋值 -->
<variable
name="bean"
type="com.rongc.wan.ProjectList" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_project"
android:layout_width="120dp"
android:layout_height="250dp"
android:layout_marginStart="15dp"
android:layout_marginTop="@dimen/dp_10"
android:scaleType="centerCrop"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:url="@{bean.envelopePic}"
tools:src="https://juejin.cn/post/@color/black_30" />
<TextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginTop="@dimen/dp_10"
android:layout_marginEnd="15dp"
android:ellipsize="end"
android:maxLines="2"
android:text="@{bean.title}"
android:textColor="#353535"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/iv_project"
app:layout_constraintTop_toTopOf="@id/iv_project"
tools:text="project name" />
<TextView
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="20dp"
android:ellipsize="end"
android:text="@{bean.desc}"
android:textColor="@color/black_40"
app:layout_constraintBottom_toTopOf="@id/tv_author"
app:layout_constraintEnd_toEndOf="@id/tv_name"
app:layout_constraintStart_toStartOf="@id/tv_name"
app:layout_constraintTop_toBottomOf="@id/tv_name"
app:layout_constraintVertical_bias="0"
tools:text="describe" />
<TextView
android:id="@+id/tv_author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:text="@{bean.author}"
app:layout_constraintBottom_toBottomOf="@id/iv_project"
app:layout_constraintStart_toStartOf="@id/tv_name"
tools:text="author" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.publishTimeStr}"
app:layout_constraintBaseline_toBaselineOf="@id/tv_author"
app:layout_constraintEnd_toEndOf="@id/tv_name"
tools:text="time" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
复制代码
- 5.列表ItemBinder
class ProjectItemBinder : BaseItemBindingBinder<ItemProjectListBinding, ProjectList>() {
override fun convert(
binding: ItemProjectListBinding, holder: BaseViewHolder, data: ProjectList
) {
// ui绑定都在xml中做好,这里不需要其他实现
}
}
复制代码
以上就是UI相关的全部代码,这是运行效果
如果仅需要实现列表页面则只有3、4、5三步。
三、详解
《如何优化多层继承造成的耦合问题》中提到利用Lifecycle的生命周期感知能力优化通过聚合方式扩展功能的使用体验——IAbility。这里的列表功能也是Ability的其中一种(ListAbility/PagerAbility)。ListAbility的主体是RecyclerView,PagerAbility的主体时ViewPager2,其他几乎一致,这里以ListAbility作分析。
先看下ListAbility的结构:
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
喜欢就支持一下吧
相关推荐