学更好的别人,
做更好的自己。
——《微卡智享》
本文长度为3942字,预计阅读9分钟
前言
前阵子做的新产品用使用BaseQuickAdapter中的basemultiitemquickadapter来实现二级列表,网的这个相关的资料也挺多,使用的版本是2.9.4,在使用中发现当二级列表展开时对子列表数据进行操作后,列表中展开和闭合显示的数据会异常。因为当时项目比较急,3.0版本的用法和原来完全不一样了,加上这个问题不大,所以就没再处理,现在抽出空来就想试试3.0版本的实现方式,毕竟作者说3.0使用kotlin重写了不少,并解决了一些2.0版本中的BUG。
实现效果
上图中可以看出,在展开列表中做删除明细的操作后,数据显示的就异常了,这个是在2.X的版本中出现的问题,现在3.0里面还是有这个情况。
不过这个倒不是本篇的重点,实际用到的这个场景应该比较少,本篇主要是说一下3.0版本中多级列表的实现方式,另外这个Demo中也是初次尝试MutableSharedFlow的来替代LiveData,使用中有些还没理解明白,管他叫经,先用了再说。
代码实现
![4345e7dbfc89be099ee320ca201d0a34.png](https://i-blog.csdnimg.cn/blog_migrate/c4374bf783378bc18abc245623d3a9e2.png)
微卡智享
在2.x版本中,使用多级列表用的BaseMultiItemQuickAdapter,定义的类要继承自MultiItemEntity,现在3.0版本中,我使用的是BaseNodeAdapter,定义的类也是继承自BaseNode和BaseExpandNode。
01
Demo总览
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
更新MutableSharedFlow的数据时,使用了tryEmit方式
加入了初始化生成数据,插入一条数据和删除一条数据的三个方法,完整的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
监听Flow的数据改变,需要在onStart中实现,在onStop中还有停止监听
其实像我这么简单的数据来说,感觉还是用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()
}
}
TIPS
这两天也是抽空在做Android使用OpenCV4.5.4的人脸识别功能,开始还想做个简单的Demo,结果现在越做感觉越有点麻烦,还要再花些时间了,等完成会第一时间更新。
完
扫描二维码
获取更多精彩
微卡智享
「 往期文章 」