Android BaseQuickAdapter3.0.4版本二级列表的使用及遇到的问题

学更好的别人,

做更好的自己。

——《微卡智享》

487696a2d56bef869151b3f02aaf4c79.png

本文长度为3942,预计阅读9分钟

前言

前阵子做的新产品用使用BaseQuickAdapter中的basemultiitemquickadapter来实现二级列表,网的这个相关的资料也挺多,使用的版本是2.9.4,在使用中发现当二级列表展开时对子列表数据进行操作后,列表中展开和闭合显示的数据会异常。因为当时项目比较急,3.0版本的用法和原来完全不一样了,加上这个问题不大,所以就没再处理,现在抽出空来就想试试3.0版本的实现方式,毕竟作者说3.0使用kotlin重写了不少,并解决了一些2.0版本中的BUG。

11e815bfad5f44911c279531ed5f598f.png

实现效果

db6cfab73030b36ad61655efef9db20f.gif

‍上图中可以看出,在展开列表中做删除明细的操作后,数据显示的就异常了,这个是在2.X的版本中出现的问题,现在3.0里面还是有这个情况。

不过这个倒不是本篇的重点,实际用到的这个场景应该比较少,本篇主要是说一下3.0版本中多级列表的实现方式,另外这个Demo中也是初次尝试MutableSharedFlow的来替代LiveData,使用中有些还没理解明白,管他叫经,先用了再说。

代码实现

4345e7dbfc89be099ee320ca201d0a34.png

微卡智享

在2.x版本中,使用多级列表用的BaseMultiItemQuickAdapter,定义的类要继承自MultiItemEntity,现在3.0版本中,我使用的是BaseNodeAdapter,定义的类也是继承自BaseNode和BaseExpandNode。

01

Demo总览

33c4e48cd3872d5f70860c5e48e3af28.png

build.gradle中加入引用项

implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4'


    //使用协程
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1"
    
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'

02

类的定义

Body子列表继承自BaseNode,因为是最末级了,所以childNode设置为NULL

class Body : BaseNode() {


    var code: String = ""
    var name: String = ""
    var specs: String = ""
    var batchno: String = ""
    var qty: Int = 0


    override val childNode: MutableList<BaseNode>?
        get() = null
}

Head父级列表继承自BaseExpandNode,其中要定义其明细列表MutableList<BaseNode>

class Head : BaseExpandNode() {


    var Bodys: MutableList<BaseNode>? = null


    var deptno: String = ""
    var deptname: String = ""


    override val childNode: MutableList<BaseNode>?
        get() = Bodys
}

03

adapter适配器

DataAdatper适配器继承自BaseNodeAdapter,使用NodeProvider将一级列表Head和二级列表Body分开写,代码看起来也方便些。

class DataAdapter : BaseNodeAdapter {


    constructor() : super() {
        addNodeProvider(HeadProvider())
        addNodeProvider(BodyProvider())
    }


    override fun getItemType(data: List<BaseNode>, position: Int): Int {
        val node = data[position]
        if (node is Head) {
            return 1
        } else if(node is Body){
            return 2
        }
        return -1;
    }
    
}

HeadProvider

class HeadProvider : BaseNodeProvider() {


    override val itemViewType: Int
        get() = 1


    override val layoutId: Int
        get() = R.layout.rcl_head


    override fun convert(helper: BaseViewHolder, item: BaseNode) {
        val head = item as Head
        helper.setText(R.id.tv_deptno, head.deptno)
            .setText(R.id.tv_deptname, head.deptname)
    }


    override fun onClick(helper: BaseViewHolder, view: View, data: BaseNode, position: Int) {
        super.onClick(helper, view, data, position)


        // 这里使用payload进行增量刷新(避免整个item刷新导致的闪烁,不自然)
        getAdapter()!!.expandOrCollapse(
            position,
            true,
            true,
            110
        )
    }
}

BodyProvider

class BodyProvider: BaseNodeProvider() {


    override val itemViewType: Int
        get() = 2
    override val layoutId: Int
        get() = R.layout.rcl_body


    override fun convert(helper: BaseViewHolder, item: BaseNode) {
        val body = item as Body
        helper.setText(R.id.tv_code, body.code)
            .setText(R.id.tv_name, body.name)
            .setText(R.id.tv_specs, body.specs)
            .setText(R.id.tv_qty, "${body.qty}")
    }
}

04

ViewModel中使用Flow

ViewModel类中定义了MutableSharedFlow

f1f75ebcf91037876f2aae191c947633.png

更新MutableSharedFlow的数据时,使用了tryEmit方式

d0e8974e3df19aa301422cd6c3d2d595.png

加入了初始化生成数据,插入一条数据和删除一条数据的三个方法,完整的ViewModel代码

class DataViewModel: ViewModel() {


    val DataList = MutableSharedFlow<MutableList<BaseNode>>(
        replay = 1,
        onBufferOverflow = BufferOverflow.DROP_OLDEST
    )


    fun InitDataList() {
        val headlist = ArrayList<BaseNode>()
        for (i in 1..3) {
            val tmphead = Head()
            tmphead.deptno = "000$i"
            tmphead.deptname = "部门$i"
            tmphead.Bodys = mutableListOf()


            for (k in 1..5) {
                val tmpbody = Body()
                tmpbody.code = "1000$k"
                tmpbody.name = "产品$k"
                tmpbody.batchno = "20231101-$k"
                tmpbody.specs = "20ml"
                tmpbody.qty = i * k


                tmphead.Bodys?.add(tmpbody)
            }


            tmphead.isExpanded = false
            headlist.add(tmphead)
        }


        DataList.tryEmit(headlist);
    }




    fun AddBody(num:Int) {
        val headlist = DataList.replayCache.first()
        if (headlist.size > 0) {
            val head = headlist.get(0)
            val tmpbody = Body()
            tmpbody.code = "1000$num"
            tmpbody.name = "新建产品$num"
            tmpbody.batchno = "20231101-$num"
            tmpbody.specs = "20ml"
            tmpbody.qty = num


            head.childNode?.add(tmpbody)
        }
        DataList.tryEmit(headlist);
    }


    fun DelBody() {
        val headlist = DataList.replayCache.first()
        if (headlist.size > 0) {
            val head = headlist.get(0)
            head.childNode?.let {
                if (it.size > 0) {
                    it.removeAt(0)
                }
            }
        }
        DataList.tryEmit(headlist);
    }
}

05

Activity中监听刷新数据

在Activity中先定义LifecycleCoroutineScope

99cf0ae4d8fc1cc8a49475e474ef1ee3.png

监听Flow的数据改变,需要在onStart中实现,在onStop中还有停止监听

9c00c43e49fdc4112a162d2938a04aa4.png

其实像我这么简单的数据来说,感觉还是用LiveData更方便,而且使用起来也更简单。

完整的Activity代码

class MainActivity : AppCompatActivity() {


    private val btn1:Button by lazy { findViewById(R.id.btn1) }
    private val btn2:Button by lazy { findViewById(R.id.btn2) }
    private val recyclerView:RecyclerView by lazy { findViewById(R.id.recycler_view) }


    private var lifecyclejob : LifecycleCoroutineScope? = null;


    private lateinit var viewModel: DataViewModel
    private lateinit var adapter: DataAdapter




    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)


        lifecyclejob = lifecycleScope


        viewModel = DataViewModel()


        viewModel.InitDataList()
        adapter = DataAdapter()


        adapter.setList(viewModel.DataList.replayCache.first())


        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.adapter = adapter


        btn1.setOnClickListener {
            viewModel.AddBody(10)
        }


        btn2.setOnClickListener {
            viewModel.DelBody()
        }
    }


    override fun onStart() {
        super.onStart()


        lifecyclejob?.let {
            it.launch {
                viewModel.DataList
                    .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
                    .collect {
                        adapter.setDiffNewData(it)
                    }
            }
        }


    }


    override fun onStop() {
        lifecyclejob?.let {
            it.cancel()
        }
        super.onStop()


    }
}

4e7c8b2a2e3dd36a0365c29790e3fa10.png

TIPS

这两天也是抽空在做Android使用OpenCV4.5.4的人脸识别功能,开始还想做个简单的Demo,结果现在越做感觉越有点麻烦,还要再花些时间了,等完成会第一时间更新。

bd8556b8ca2883c188ff27e5f86a8dbc.png

扫描二维码

获取更多精彩

微卡智享

7aab0354e94fe7c00a848465981e8c27.png

「 往期文章 」

Android Room数据库版本迁移的实战

关于Android录屏程序在Android10下的修改

Android制作带悬浮窗控制的录屏程序Demo

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vaccae

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值