本项目主要做了RecyclerView adapter、ListView adapter、ViewPager adapter fragment的封装,旨在减少大量模板代码。
也加了和这些相关连带问题的解决方法:adapter套RecyclerView回调繁琐、动态fragment刷新白屏、ViewPager和ViewPager2高度不能wrap等问题的解决
Adapter属于View层,Activity也是View层,在当前各种新框架下两个几乎没有什么代码,然而它们之间交互起来特别繁琐,如果我们直接将Adapter合并到Activity里,会产生什么意想不到的结果呢?
一个简单的listAdapter只需要如下一行(没看错,总共就一行),基于ViewBinding
class MainActivity : AppCompatActivity() {
//lazyNone就是lazy(LazyThreadSafetyMode.NONE, initializer)的拓展
private val listAdapter by lazyNone {
createListVbAdapter<AdapterMainListBinding, String> { holder, vb, bean -> }
}
}
点击事件什么的都不需要额外操作,没有回调也不需要额外传参,直接在Adapter写就行了
class MainActivity : AppCompatActivity() {
private val listAdapter by lazyNone {
createListVbAdapter<AdapterMainListBinding, String> { holder, vb, bean ->
holder.itemView.setBackgroundColor(if (bean.contains("10")) 0xff999999.toInt() else 0xffffffff.toInt())
vb.tvText.text = bean
vb.btButton.setOnFastClickListener {
toast("点击了Button")
}
holder.setOnFastClickListener {
toast("点击item了${holder.listPosition}")
}
}.apply {
//自带header、footer
headerView = AppCompatTextView(this@MainActivity).apply {
text = "这是header"
}
}
}
}
只有2个方法:addMultipleItem、addDefaultMultipleItem
addMultipleItem的isThisTypeCallback返回一个布尔值表示是否是当前条目,不需要额外其他逻辑
小提示:对于addMultipleItem这种多参数idea无法自动快速提示出来,可以使用“Ctrl+空格”来主动弹出。但是由于Windows占用了这个快捷键,请打开“设置> Keymap>Main Menu>Code>Code Completion>Basic”来重新设置,比如:“Ctrl+;”、“Ctrl+,”
private val multiAdapter by lazyNone {
createMultiAdapter<TestBean>().apply {
addMultipleItem<AdapterMainMultiple0Binding>(isThisTypeCallback = { listPosition, _ -> listPosition % 3 == 0 }) { holder, vb, bean ->
vb.tvText.setTextColor(0xff00ff00.toInt())
vb.tvText.text = "多条目0:${bean.text}"
holder.setOnFastClickListener {
toast("点击item了${holder.listPosition}")
}
}
addMultipleItem<AdapterMainMultiple1Binding>(isThisTypeCallback = { listPosition, _ -> listPosition % 3 == 1 }) { holder, vb, bean ->
vb.tvText2.text = "多条目1:${bean.text}"
}
//多条目兜底,上面判断完后的剩余情况防止出错,如:后端新增了新的数据类型
addDefaultMultipleItem<AdapterMainMultipleDefBinding>()
headerView = AppCompatTextView(this@MainActivity).apply { text = "这是header" }
footerView = AppCompatTextView(this@MainActivity).apply { text = "这是footer" }
}
}
注意:嵌套效率问题暂且不在当前讨论范围内
private val nesAdapter by lazyNone {
createListVbAdapter<AdapterMainNesBinding, TestBean> { holder, vb, bean ->
vb.tvNesText.text = "这是嵌套外层:${bean.text},${holder.listPosition},${holder.adapterLayoutPosition}"
val itemAdapter = vb.rvItemList.getAdapterOrCreate {
createListVbAdapter<AdapterMainNesItemBinding, String> { holder, vb, bean ->
vb.tvItem.text = "这是内层$bean,${holder.listPosition},${holder.adapterLayoutPosition}"
}.apply {
headerView = AppCompatTextView(this@MainActivity).apply {
text = "这是内层header"
setOnFastClickListener {
toast("你点击了内层header")
}
}
footerView = AppCompatTextView(this@MainActivity).apply { text = "这是内层footer" }
vb.rvItemList.setGridLayoutManagerSpanSizeLookup {
when (it) {
0, (itemCount - 1) -> 2
else -> 1
}
}
}
}
itemAdapter.notifyDataSetChanged(bean.itemTextList)
}.apply {
headerView = AppCompatTextView(this@MainActivity).apply {
text = "这是外层header"
setTextColor(0xff00ff00.toInt())
}
}
}
不推荐这么写,因为需要来回处理页面交互
class MyAdapter: BaseListAdapter<AdapterMainListBinding, String>(/*可选传list*/) {
override fun onBindListViewHolder(holder: BaseViewHolder<AdapterMainListBinding>, bean: String){
//bind
}
// 按混淆配置好,这里就不需要重写
// override fun onCreateListViewHolder(parent: ViewGroup): BaseViewHolder<AdapterMainListBinding> =
// BaseViewHolder(ViewBindingHelper.getViewBindingInstanceByClass<AdapterMainListBinding>(parent.layoutInflater, parent))
}
mVp.setAdapter(new BaseFragmentPagerAdapter(getSupportFragmentManager(), mFrags));
//或
mVp.setAdapter(new BaseFragmentPagerAdapter(getSupportFragmentManager(), frag1,frag2...));
//动态修改frag
mAdapter = new BaseFragmentStatePagerAdapter(getSupportFragmentManager(), mFrags);
mVp.setAdapter(mAdapter);
...
mAdapter.getFragments().add(xxx);//由于内部有新的list,所以并不能用自己的mFrags
mAdapter.getFragments().remove(yyy);
mAdapter.notifyDataSetChanged();
//解决动态修改刷新白屏的问题
BaseFragmentNotifyAdapter adapter = new BaseFragmentNotifyAdapter(getSupportFragmentManager(), mFrags);
mVp.setAdapter(adapter);
...
adapter.notifyAllItem(1);//保留展示的frag这样就不会白屏了,想要刷新这个frag当然需要自己frag内部刷新了,详见app下的示例
多条目太复杂,无法满足?
当然还有适用于各种复杂样式的adapter容器(如:聊天列表,首页、今日头条的列表等):
本项目已内置为kotlin版(待后续继续简化),直接使用即可: 一个通过add其他adapter的超级容器,无论多么复杂的列表样式均可解耦成一个一个的adapter
简单示例(具体请看详情介绍):
mRv.setLayoutManager(LinearLayoutManager(this))//如果是GridLayoutManager需要提前设置好,Linear随意
val baseAdapter = createContainerAdapter()
mRv.setAdapter(baseAdapter.addAdapter(TextAdapter(),ImageAdapter()))
//...
baseAdapter.setListAndNotifyDataSetChanged(list)
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/vp_pager"
android:layout_width="match_parent"
android:layout_height="1dp"/>//随便写一个高
//初始化时即可调用(如果child高度因数据再次变化,这里不会更新,请在合适时机再次调用,后续可能会进行优化)
ViewGroupWrapUtils.wrap(vp, false);
本项目所有的adapter都会内部维护一个List,所以修改数据请一定要使用adapter.getList(),然后notify...
刷新list数据建议调用notifyListItem...方法,这样就不用处理header、footer数量了
关于增加多个header、footer:个人认为多个header、footer场景少并且双方都难以管理,所以请自行将header、footer设置成LinearLayout,然后自行添加维护
关于空状态:个人认为这不在adapter范畴(对上拉下拉、notifyItem等都会有交互问题实际上并不友好),自行写个空状态工具类反而更方便和简单(如有需要后续开放)
你的build.gradle要有jitpack.io,大致如下
allprojects {
repositories {
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
maven { url 'https://jitpack.io' }
google()
jcenter()
}
}
然后:
(api或)implementation 'com.github.weimingjue:BaseAdapter:5.0.0'
混淆要求: #请保留ViewBinding里的两个inflate方法,不然会反射不到 -keepclassmembers class * extends androidx.viewbinding.ViewBinding{ public static ** inflate(...); } #如果还是使用旧的那种继承方式请再保留ViewBinding类(createXxx不需要加) -keep class * extends androidx.viewbinding.ViewBinding