Skip to content

RecyclerView、ViewPager adapter的极简化(简单到你无法想象),主要基于AndroidX ViewBinding。功能:统一adapter方法,合并到Activity层;对vp的fragAdapter终极封装简单便捷不需要任何额外逻辑。(ViewBinding Adapter)

Notifications You must be signed in to change notification settings

weimingjue/BaseAdapter

Repository files navigation

代码非常简单,基于dataBinding

概览

本项目主要做了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))
}

ViewPager的Fragment更简单

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)

ViewPager、RecyclerView、ViewPager2高度自适应第一个child

<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

About

RecyclerView、ViewPager adapter的极简化(简单到你无法想象),主要基于AndroidX ViewBinding。功能:统一adapter方法,合并到Activity层;对vp的fragAdapter终极封装简单便捷不需要任何额外逻辑。(ViewBinding Adapter)

Topics

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages