ListAdapter封装 (三) - 多条目, 头尾, 嵌套, 单多选.

前言:

上一篇文章已经讲解 SimpleAdapter 的基本封装.  这次我们将用 ConcatAdapter 封装头尾,  并封装多类型Adapter,  嵌套RecycleView, 单多选Adapter;

目录:

  1. 用 ConcatAdapter 封装头尾;
  2. 用泛型, 改建 BaseAdapter
  3. 多条目 MultipleAdapter
  4. 嵌套  NestedAdapter
  5. 单选  SingleChoice
  6. 多选  MultipleChoice

1.用 ConcatAdapter 封装头尾;

题外话: 博主之前学 ListAdapter 的时候, 为封装头尾可费了老劲 [撇嘴] ;

一开始用多条目类型, 汇总条目数的方式; 然后 头是出来了,但是首次刷列表会自动滚到底???  博主四眼懵逼. 也没找到原因;

然后 博主改用假实体的方式, 单独设置头尾对象,  重写 submitList 带上头尾实体计算.  最终Ok,  想的是MVVM对 position不是太敏感了.  然后出现了 ConcatAdapter  [鼓掌]

1.1 ConcatAdapter  顺序的连接其他 Adapter:

ConcatAdapter 是 recyclerview: 1.2.0-alpha 04 中提供的一个新组件;

它可以帮我们顺序地组合多个 Adapter,并让它们显示在同一个 RecyclerView 中。

想了解小伙伴 点这里

recyclerview = '1.2.0'
implementation androidx.recyclerview:recyclerview:${recyclerview}

1.2 思路:

以前我们头尾是单独 ViewType;  现在呢,它们变成了单独的 Adapter;  所以, 现在要创建 单条目的Adapter

EndAdapter: 只有一个条目, 也不需要绑定数据, View由Activity或Fragment控制;

EndHolder: 继承 ViewHolder, 传递View即可

/**
 * 头尾 Adapter; View由外部 维护;
 */
class EndAdapter(val view: View) : RecyclerView.Adapter<EndHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = EndHolder(view)
override fun onBindViewHolder(holder: EndHolder, position: Int) {}
override fun getItemCount() = 1
} class EndHolder(itemView: View) : RecyclerView.ViewHolder(itemView)

1.3 在我们的 BaseAdapter加入: 

withHeaderAndFooter()  传入头尾View 就OK了;

/**
* 组装头尾, 返回一个新的 ConcatAdapter 实体, 需要将新实体设置给 RecycleView
*/
fun withHeaderAndFooter(
header: View? = null,
footer: View? = null
) = when{
header != null && footer != null -> ConcatAdapter(EndAdapter(header), this, EndAdapter(footer))
header != null && footer == null -> ConcatAdapter(EndAdapter(header), this)
header == null && footer != null -> ConcatAdapter(this, EndAdapter(footer))
else -> this
}

1.4 使用: 

只需要将 withHeaderAndFooter() 返回的 Adapter 赋给 RecycleView即可,  我们还是操作 主Adapter

val headBinding = ViewHeaderTjBinding.inflate(LayoutInflater.from(mActivity), mView as @Nullable ViewGroup, false)

//主数据 Adapter
mAdapter = MultipleAdapter() //将这个由 withHeaderAndFooter() 返回的 ConcatAdapter 赋给 RecycleView 即可
val mmAdapter = mAdapter.withHeaderAndFooter(headBinding.root)
mDataBind.rvRecycle.let {
it.layoutManager = LinearLayoutManager(mActivity)
it.adapter = mmAdapter
}

2. BaseAdapter 改造, 传入实体泛型;

2.1 首先 DiffCallback 必须要用泛型改造;

class DiffCallback<T : BaseItem>: DiffUtil.ItemCallback<T>() {
/**
* 比较两个条目对象 是否为同一个Item
*/
override fun areItemsTheSame(oldItem: T, newItem: T): Boolean {
return oldItem === newItem
} /**
* 再确定为同一条目的情况下; 再去比较 item 的内容是否发生变化;
* 我们使用 状态标识方式判断;
* @return true: 代表无变化; false: 有变化;
*/
override fun areContentsTheSame(oldItem: T, newItem: T): Boolean {
return !oldItem.hasChanged
}
}

2.2 在 BaseAdapter 中加入方便本地操作的 增删改 方法;  最终代码如下

abstract class BaseAdapter<T: BaseItem>(
protected val handler: BaseHandler? = null) :
ListAdapter<T, NewViewHolder>(DiffCallback()) { override fun onBindViewHolder(holder: NewViewHolder, position: Int) {
holder.bind(getItem(position))
} /**
* 重写 提交数据方法, 让它必定以新数据集合对象传入
*/
override fun submitList(list: MutableList<out T>?) {
val newData = mutableListOf<T>()
if(list != null){
newData.addAll(list)
}
super.submitList(newData)
} /**
* 删除指定条目
*/
fun remove(entity: BaseItem){
removeAt(currentList.indexOf(entity))
}
fun removeAt(position: Int){
if(position == -1) return
if(position >= currentList.size) return
val newData = mutableListOf<T>()
newData.addAll(currentList)
newData.removeAt(position)
super.submitList(newData)
} /**
* 修改指定条目
*/
fun update(entity: T){
updateAt(currentList.indexOf(entity))
}
fun updateAt(position: Int){
if(position == -1) return
if(position >= currentList.size) return
notifyItemChanged(position)
} /**
* 新增条目
*/
fun insert(entity: T, position: Int = -1){
val newData = mutableListOf<T>()
newData.addAll(currentList) if(position < newData.size && position >= 0){
newData.add(position, entity)
}else{
newData.add(entity)
}
super.submitList(newData)
} /**
* 组装头尾, 返回一个新的 ConcatAdapter 实体, 需要将新实体设置给 RecycleView
*/
fun withHeaderAndFooter(
header: View? = null,
footer: View? = null
) = when{
header != null && footer != null -> ConcatAdapter(EndAdapter(header), this, EndAdapter(footer))
header != null && footer == null -> ConcatAdapter(EndAdapter(header), this)
header == null && footer != null -> ConcatAdapter(this, EndAdapter(footer))
else -> this
}
}

2.3 SimpleAdapter 只需要改成 继承  BaseAdapter<BaseItem> 即可; 

open class SimpleAdapter(
private val layout: Int,
handler: BaseHandler? = null
) : BaseAdapter<BaseItem>(handler)

3. 多条目类型 MultipleAdapter

多类型其实是很简单的, 还是重写 getItemViewType();  

有区别的是, getItemViewType() 的返回值, 直接返回 布局文件ID.  然后实体类自行判断给出 LayoutId

3.1 实体类:  

还是实现  BaseItem,  重写  getMItemType() , 返回布局文件 ID

class MultipleEntity(
var name: String,
var index: Int = 0,
override var hasChanged: Boolean = false)
: BaseItem { /**
* 实体类自行判断, 并给出 布局id
*/
override fun getMItemType(): Int {
return if(index % 2 == 0){
R.layout.item_multiple_one
}else{
R.layout.item_multiple_two
}
}
}

3.2  MultipleAdapter:

需要重写 getItemViewType() 并将实体类的 getMItemType() 结果返回;

创建 ViewHolder 的时候,  直接用 viewType 作为布局ID (因为我们用布局ID做的ViewType)

/**
* 多条目 类型 Adapter;
* 1.实体类需要重写 {@link BaseItem} 的 getMItemType() 方法; 并根据类型,返回不同的 LayoutId
* 2.复杂 Adapter. 还需要自定义; 继承 MultipleAdapter 并重写 getItemViewType()
*/
open class MultipleAdapter(handler: BaseHandler? = null) :
BaseAdapter<BaseItem>(handler) { /**
* Item类型, 同 layoutId;
*/
override fun getItemViewType(position: Int): Int {
return currentList[position].getMItemType()
} /**
* viewType 同 layoutId
*/
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewViewHolder {
if(viewType == 0) throw RuntimeException("Undefined itemViewType")
return NewViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
viewType, parent, false
), handler
)
}
}

3.3 接下来就是用了; 

很简单是不是, 直接把数据集合塞进Adapter里 就完事了

mAdapter = MultipleAdapter()

mDataBind.rvRecycle.let {
it.layoutManager = LinearLayoutManager(mActivity)
it.adapter = mAdapter
}

val data = mutableListOf<MultipleEntity>()
repeat(30){
if(it % 2 == 0){
data.add(MultipleEntity("小美", it))
}else{
data.add(MultipleEntity("小状", it))
}
}
mAdapter.submitList(data)

3.4 布局文件也帖出来吧

item_multiple_one.xml
<layout>
<data>
<variable
name="item"
type="com.example.kotlinmvpframe.yiyou.entity.MultipleEntity" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:padding="12dp">
<TextView
style="@style/tv_base_16_dark"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{item.name + " 我是类型one,我有靓照"}'
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<ImageView
style="@style/img_wrap"
android:layout_width="60dp"
android:layout_height="50dp"
android:src="@drawable/bg"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout> item_multiple_two.xml
<layout>
<data>
<variable
name="item"
type="com.example.kotlinmvpframe.yiyou.entity.MultipleEntity" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:padding="12dp">
<TextView
style="@style/tv_base_16_dark"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{item.name + " 我是类型two,我有money"}'
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<TextView
style="@style/tv_base_16_dark"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="italic|bold"
android:textColor="@color/shape_red"
android:text="¥. 500"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

3.5 再贴上效果图 : [奸笑]

4. 嵌套  NestedAdapter

4.1 嵌套 RecycleView 的注意点: 

嵌套子层数据集合:  一般在外层实体中;  例如 一条朋友圈中多张图片.  外层实体持有 Image集合;  我们就用接口的方式, 规范这个集合

子层RecycleView: 如果固定的 ViewDataBinding 对象, 我们可以直接拿它的 RecycleVIew.  但是 它不固定咋办?  也没法封装继承啊!   好吧, 好办 [机智]

子层的 Adapter:  这个好办, 上一篇我们已经封装好了 SimpleAdapter; 

子层列表事件:  子层的事件, 还是用 handler 处理, 但最终由回调函数,调到主界面处理;

4.2 思路

首先外层列表的主数据绑定, 没有变化, 还是用总的 ViewHolder.bind() 方式;

其次我们规范 嵌套子层 RecycleView 的ID, 用 findViewById 的方式 把它存到 ViewHolder 里;

实体类实现  BaseNestedItem 接口的  getNestedList() 函数;  把子层集合返回; 

4.3 上代码:

/**
* 单条目, 简单Item MVVM Adapter
* 复杂类型 还是得自定义 Adapter
*/
open class NestedAdapter<T: BaseNestedItem>(
/**
* 外层列表 item布局;
*/
private val layout: Int,
/**
* 内层列表 item布局;
*/
private val nestedLayoutId: Int,
/**
* 生成子层Recycle的 LayoutManager; 可以是 LinearLayoutManager GridLayoutManager 等;
* 默认 横向 LinearLayoutManager
*/
private val childLayoutManager: (() -> RecyclerView.LayoutManager)? = null,
/**
* 子层列表点击回调; 此方式可能不太优雅. 有待改进
* arg1 子层点击 View
* arg2 外层点击 position; 这个position并不保险; 当存在 Header 时, 它需要 -1
* arg3 子层实体对象
*/
private val childClickListener: ((View, Int, BaseItem) -> Unit)? = null,
/**
* 外层类表点击事件;
*/
handler: BaseHandler? = null ) :
BaseAdapter(handler) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewViewHolder {
val bingding: ViewDataBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.context), layout, parent, false)
// 统一规范 子层 RecycleView 的id; 将 RecyclerView 存入 NestedHolder
val rvNested = bingding.root.findViewById<RecyclerView>(R.id.rv_nested_item)
return NestedHolder(rvNested, bingding, handler)
} override fun onBindViewHolder(holder: NewViewHolder, position: Int) {
if(holder is NestedHolder){
//绑定子层 Recycle Adapter
createNestedAdapter(holder)
//绑定子层 数据
(holder.rvNested?.adapter as SimpleAdapter?)?.submitList(getItem(position).getNestedList())
}
super.onBindViewHolder(holder, position)
} /**
* 绑定子层 Recycle 的 Adapter
*/
private fun createNestedAdapter(holder: NestedHolder) {
holder.rvNested?.let {
if(it.adapter == null){
//子层点击事件; null 则不设置;
val handler = if(childClickListener == null){
null
}else{
object : Handler<BaseItem>(){
override fun onClick(view: View, info: BaseItem) {
childClickListener.invoke(view, holder.layoutPosition, info)
}
}
}
val adapter = SimpleAdapter(nestedLayoutId, handler)
it.layoutManager = childLayoutManager?.invoke() ?: LinearLayoutManager(it.context, RecyclerView.HORIZONTAL, false)
it.adapter = adapter
}
}
} /**
* 重写 ViewHoleder, 附带 RecyclerView
*/
class NestedHolder(val rvNested: RecyclerView?, binding: ViewDataBinding, handler: BaseHandler?) : NewViewHolder(binding, handler)
}

4.4 讲解: 

可以看出, NestedAdapter 的内外层布局 ID直接传入;  内层 LayoutManager, 及事件响应 由kotlin高阶函数提供;

注意: 内层事件响应时, 回调给了外层position 是通过  holder.layoutPosition;  但是在有头部的Adapter中 layoutPosition 是需要 -1 的;

还有一个 holder.bindingAdapterPosition 参数, 但是它在 带头部的ConcatAdapter 中使用时, 经常返回 -1; 不知道是不是博主使用姿势不对;

4.5 BaseNestedItem:   getNestedList() 函数统一规范子集合;

interface BaseNestedItem : BaseItem{
fun getNestedList(): MutableList<out BaseItem>?
}

4.6 实体类:

class MultipleEntity(
var name: String,
override var hasChanged: Boolean = false)
: BaseNestedItem { var imgs: MutableList<ImageEntity>? = null // 返回子层集合
override fun getNestedList(): MutableList<ImageEntity>? = imgs /**
* 子层集合实体类
*/
class ImageEntity(val res: Int, override var hasChanged: Boolean = false) : BaseItem
}

4.7 使用: 

mAdapter = NestedAdapter<MultipleEntity>(
//主列表布局 ID
R.layout.item_nested_one,
//子列表布局 ID
R.layout.item_img,
//事件响应
childClickListener = {
_, layoutPosition, info ->
val childIndex = (mAdapter.currentList[layoutPosition] as MultipleEntity).imgs?.indexOf(info) ?: 0
Toast.makeText(mActivity.applicationContext, "点击了第${layoutPosition + 1} 的第${childIndex + 1}个条目", Toast.LENGTH_SHORT).show()
})
mDataBind.rvRecycle.let {
it.layoutManager = LinearLayoutManager(mActivity)
it.adapter = mAdapter
} val data = mutableListOf<MultipleEntity>()
repeat(5){
val entity = if(it % 2 == 0){
MultipleEntity("小美", it)
}else{
MultipleEntity("小状", it)
}
entity.imgs = mutableListOf(
ImageEntity(R.drawable.bg), ImageEntity(R.drawable.bg), ImageEntity(R.drawable.bg), ImageEntity(R.drawable.bg),
ImageEntity(R.drawable.bg), ImageEntity(R.drawable.bg), ImageEntity(R.drawable.bg), ImageEntity(R.drawable.bg))
data.add(entity)
}
mAdapter.submitList(data)

4.8 布局贴出来

item_nested_one.xml
<layout>
<data>
<variable
name="item"
type="com.example.kotlinmvpframe.yiyou.entity.MultipleEntity" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:padding="12dp">
<TextView
android:id="@+id/tv_name_item"
style="@style/tv_base_16_dark"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{item.name}'
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_nested_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:layout_constraintTop_toBottomOf="@id/tv_name_item"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout> item_img.xml
<layout>
<data>
<variable
name="item"
type="com.example.kotlinmvpframe.yiyou.entity.MultipleEntity.ImageEntity" />
<variable
name="handler"
type="com.example.kotlinmvpframe.test.testtwo.Handler" />
</data>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{v -> handler.onClick(v, item)}"
android:padding="8dp">
<ImageView
style="@style/img_wrap"
android:layout_width="80dp"
android:layout_height="80dp"
app:imgRes="@{item.res}"/>
</FrameLayout>
</layout>

4.9 效果图来了: 

5. SingleChoiceAdapter 单选 

5.1  分析: 

  1. 实体需要有 checked 字段, 我们用 BaseCheckedItem 作为实体基类
  2. 规范 布局文件中 CheckBox 的 ID,  onCreateViewHolder 时 通过 findViewById 将控件保存到 ViewHolder
  3. 选中某个条目时, 之前选中的条目需要反选.  所以必须要知道上一次选中的 实体和CheckBox
  4. 监听 onCheckedChanged 事件,  新实体选中, 就实体反选.
  5. 单选时, 不允许反选, 此时不用 CheckBox,  而用 RadioButton

5.2 BaseCheckedItem 

它是  BaseItem 的实现类,  并增加了 hasChecked 字段

/**
* 普通单多选列表 实体类
*/
interface BaseCheckedItem : BaseItem{
var hasChecked: Boolean // 是否被勾选中
}

5.3 实体类

class CheckEntity(
var name: String? = null,
override var hasChecked: Boolean = false,
override var hasChanged: Boolean = false)
: BaseCheckedItem

5.4 重点来了:  SingleChoiceAdapter

直接上代码吧, 已经写了注释

class SingleChoiceAdapter<T : BaseCheckedItem>(
/**
* 布局id;
*/
private val layout: Int,
handler: BaseHandler? = null
): BaseAdapter<T>(handler), CompoundButton.OnCheckedChangeListener {
//弱引用, 当选中条目被删除时, 应当释放资源
var wearEntity: WeakReference<T>? = null
var wearView: WeakReference<CompoundButton>? = null override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewViewHolder {
val bingding: ViewDataBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.context), layout, parent, false)
// 统一规范 CheckBox 的id; 将 RecyclerView 存入 ChoiceHolder
val cbCheck = bingding.root.findViewById<CompoundButton>(R.id.cb_check_item)
return ChoiceHolder(cbCheck, bingding, handler)
} override fun onBindViewHolder(holder: NewViewHolder, position: Int) {
if(holder is ChoiceHolder){
val entity = currentList[position]
if(entity.hasChecked){
wearEntity = WeakReference(entity)
wearView = WeakReference(holder.cbCheck)
}
//用 tag 将对象实体存储;
holder.cbCheck?.tag = entity
holder.cbCheck?.setOnCheckedChangeListener(this) //这一行是测试用的, 应当删掉
(entity as CheckEntity).name = Integer.toHexString(holder.cbCheck?.hashCode() ?: 0)
}
super.onBindViewHolder(holder, position)
} /**
* checkbox 选中事件
*/
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
//判断是否是 手动操作
if(buttonView?.isPressed == true){
Log.d("pppppppppppppppppp", "手动操作了 ChekcBox") wearEntity?.let {
it.get()?.hasChecked = false
it.clear()
} // 因 RecycleView 复用机制, 这里新旧 Button 可能为同一个;
// 当同一个button 时, 不需要重置原button 的选中状态; 不需要重新 new WeakReference
val isSameBtn = buttonView == wearView?.get()
if(!isSameBtn){
wearView?.let {
it.get()?.isChecked = false
it.clear()
}
wearView = WeakReference(buttonView)
} val entity = buttonView.tag as T?
entity?.hasChecked = true
wearEntity = WeakReference(entity)
}
} /**
* 重写 ViewHoleder, 附带 ImageView; 勾选是使用 ImageView 切换图片的方式
*/
class ChoiceHolder(val cbCheck: CompoundButton?, binding: ViewDataBinding, handler: BaseHandler?) : NewViewHolder(binding, handler)
}

5.5  布局文件: item_test_choise

<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> <data>
<variable
name="item"
type="com.example.kotlinmvpframe.yiyou.entity.CheckEntity" /> <variable
name="handler"
type="com.example.kotlinmvpframe.test.testtwo.Handler" />
</data> <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:text="@{item.name}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<RadioButton
android:id="@+id/cb_check_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableEnd="@drawable/checkbox_selector"
android:padding="24dp"
android:background="@null"
android:button="@null"
android:checked="@{item.hasChecked}"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

5.6 使用:

很简单, 只需要 初始化 Adapter, RecycleView;  并设置数据源即可

mAdapter = SingleChoiceAdapter(R.layout.item_test_choise)
mDataBind.rvRecycle.let {
it.layoutManager = LinearLayoutManager(mActivity)
it.adapter = mAdapter
} val data = mutableListOf<CheckEntity>()
repeat(15){
data.add(CheckEntity("小华"))
}
mAdapter.submitList(data) //以下, 没用
mDataBind.btnLeft.setOnClickListener {
val entity = mAdapter.wearEntity?.get()
if(entity == null){
Toast.makeText(mActivity, "当前未选中", Toast.LENGTH_SHORT).show()
return@setOnClickListener
} val position = mAdapter.currentList.indexOf(entity)
Toast.makeText(mActivity, "当前选中${entity.name}, index=${position}", Toast.LENGTH_SHORT).show()
}
mDataBind.btnRight.setOnClickListener {
mAdapter.notifyDataSetChanged()
}

5.7 效果图:

6. MultipleChoiceAdapter 多选

多选就简单了;   注意, 需要将 item_test_choise 中的 RadioButton 换成 CheckBox

6.1 直接上代码:

class MultipleChoiceAdapter<T : BaseCheckedItem>(
/**
* 布局id;
*/
private val layout: Int,
handler: BaseHandler? = null
): BaseAdapter<T>(handler), CompoundButton.OnCheckedChangeListener { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewViewHolder {
val bingding: ViewDataBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.context), layout, parent, false)
// 统一规范 CheckBox 的id; 将 RecyclerView 存入 NestedHolder
val cbCheck = bingding.root.findViewById<CompoundButton>(R.id.cb_check_item)
return SingleChoiceAdapter.ChoiceHolder(cbCheck, bingding, handler)
} override fun onBindViewHolder(holder: NewViewHolder, position: Int) {
if(holder is SingleChoiceAdapter.ChoiceHolder){
val entity = currentList[position]
holder.cbCheck?.tag = entity
holder.cbCheck?.setOnCheckedChangeListener(this)
}
super.onBindViewHolder(holder, position)
} /**
* checkbox 选中事件
*/
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
//判断是否是 手动操作
if(buttonView?.isPressed == true){
val entity = buttonView.tag as T?
entity?.hasChecked = isChecked
}
}
}

6.2 使用跟 单选Adapter一样,  初始化RecycleVIew,  设置数据源即可;  

遍历的话这样:

val list = mAdapter.currentList.filter {
it.hasChecked
}
Toast.makeText(mActivity, "当前选中${list.size}条", Toast.LENGTH_SHORT).show()

6.3 效果图

好的! 终于 over

回到顶部

最新文章

  1. EF里的默认映射以及如何使用Data Annotations和Fluent API配置数据库的映射
  2. 【Win10 应用开发】从前台应用触发后台任务
  3. Kooboo CMS 之TextContent详解
  4. auto refresh iframe
  5. Windows Server 2008R2服务器安装及设置教程
  6. linux新内核的时钟机制代码
  7. 【转】使用Web墨卡托辅助球体切片方案的地图公共属性
  8. PHP5新语法学习
  9. Mysql 常用命令和注意事项
  10. Implementing a builder: Combine
  11. HTTPS协议入门
  12. 八大排序算法——插入排序(动图演示 思路分析 实例代码java 复杂度分析)
  13. 000 Python的运行
  14. max_execution_time with sleep
  15. docker探索-Docker 1.13 编排能力进化(十三)
  16. InstallShield打包,以及集成TFS、JenKins
  17. centos7的FTP服务vsftpd里建立虚拟用户不同目录分配不同权限
  18. Memorize and recite an important historical speech
  19. Atitit.&#160;真正的全中国文字attilax易语言的特点以及范例
  20. JAVA EE配TOMCAT

热门文章

  1. mongodb 在PHP中常见问题及解决方法
  2. foreign key 多对一 多对对 一对一
  3. MongoDB&amp;#183;Windows下管理员密码重置解决方案
  4. 基础知识:DFRduino UNO R3最全资料详解
  5. ESLint语法报错问题
  6. [bug] Authentication failed for token submission (认证失败)异常
  7. [bug] Scala eclipse:找不到或无法加载主类
  8. Linux_配置认证访问FTP服务
  9. 基于LNMP架构搭建wordpress个人博客
  10. nginx 的三种虚拟主机配置方法