MultiTypeAdapter在recycleView中的使用和点击

概述

RecycleView中多种数据结构的item进行解耦,单一职责显示多种类型的item布局
MultiTypeAdapter会给items中每一种类型的itemData找到对应的itemViewBinder进行数据绑定
基于版本 implementation ‘com.drakeet.multitype:multitype:4.2.0’

itemViewBinder

在处理item点击事件时候使用了自定义回调接口和ItemViewDelegate中的getPosition(holder)的api

package com.bliss.multitype.binder

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnimationUtils
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.bliss.multitype.app.R
import com.bliss.multitype.data.TextItem
import com.drakeet.multitype.ItemViewBinder

/**
* Author: yangtianfu
* Date: 2021/1/28 20:13
* Describe:每一种itemViewBinder绑定指定类型的data - TextItem
*/
class TextItemViewBinder(val listener:OnItemClickedListener):ItemViewBinder<TextItem,TextItemViewBinder.TextHolder>() {
    private var lastShownAnimationPosition: Int = 0
    //holder初始化item控件
   class TextHolder(itemView: View):RecyclerView.ViewHolder(itemView){
       val text:TextView = itemView.findViewById(R.id.text)
   }

    override fun onBindViewHolder(holder: TextHolder, item: TextItem) {
        holder.text.text = "hello ${item.text}"
        //item点击事件,此处position可能会返回-1,故需要做一下判断
        holder.itemView.setOnClickListener {
            val position = getPosition(holder)
            if (position>=0){
                listener.onClick(holder.text,position,item)
            }
        }
        setAnimation(holder.itemView,holder.position)
    }

    private fun setAnimation(itemView: View, position: Int) {
        if (position>lastShownAnimationPosition){
            itemView.startAnimation(AnimationUtils.loadAnimation(itemView.context,android.R.anim.slide_in_left))
            lastShownAnimationPosition = position
        }
    }

    override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): TextHolder {
        return TextHolder(inflater.inflate(R.layout.item_text,parent,false))
    }

    //view销毁时清除动画
    override fun onViewDetachedFromWindow(holder: TextHolder) {
        holder.itemView.clearAnimation()
    }
    interface OnItemClickedListener{
        fun onClick(textView: TextView,position: Int,textItem: TextItem)
    }
}
 /**
     * 单一item类型
     * android:descendantFocusability="blocksDescendants"处理item不显示的问题
     * beforeDescendants:viewgroup会优先其子类控件而获取到焦点
     * afterDescendants:viewgroup只有当其子类控件不需要获取焦点时才获取焦点
     * blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点,常用
     */
    private fun imageItemShow() {
        //注册ItemViewBinder
        adapter.register(ImageItemViewBinder())
        binding.recycleView.adapter = adapter
        //数据源
        val imageItem = ImageItem(R.mipmap.ic_launcher)
        for (i in 0..19) {
            items.add(imageItem)
        }
        adapter.items = items
        adapter.notifyDataSetChanged()
    }

MultiTypeAdapter在UI中的使用和item点击事件处理

 private fun imageAndTextItemsShow() {
        //注册ItemViewBinder,每一种数据源对应一种viewBinder,都需要进行注册
        adapter.register(ImageItemViewBinder())

        //MultiTypeAdapter中item点击事件回调处理
        val itemTextItemBinder = TextItemViewBinder(object : TextItemViewBinder.OnItemClickedListener {
            override fun onClick(textView: TextView, position: Int, textItem: TextItem) {
                Log.e("onClick ", "MultiTypeAdapter中item点击事件回调处理 - 点击了- ${textView.text} position = $position 当前item数据:${textItem.toString()}" )
            }

        })
        adapter.register(itemTextItemBinder)
        adapter.register(RichViewDelegate())
        binding.recycleView.adapter = adapter
        //多类型数据源
        val imageItem = ImageItem(R.mipmap.ic_launcher)
        val textItem = TextItem("world")
        val richItem = RichItem("自定义view做item", R.drawable.img_11)
        for (i in 0..19) {
            items.add(textItem)
            items.add(imageItem)
            items.add(richItem)
        }
        adapter.items = items //adapter会给items中每一种类型的itemData找到对应的itemViewBinder进行数据绑定
        adapter.notifyDataSetChanged()
    }

item点击效果如下
item点击回调

多种item数据类型

class ImageItemViewBinder : ItemViewBinder<ImageItem, ImageItemViewBinder.ImageHolder>() {

    //①创建holder,然后在ItemViewBinder泛型中填入改holder
    //标记为 inner 的嵌套类能够访问其外部类的成员。内部类会带有一个对外部类的对象的引用,不是单例,可以被定义多个
    inner class ImageHolder(itemView: View):RecyclerView.ViewHolder(itemView){
        val image:ImageView = itemView.findViewById(R.id.image)
    }

    override fun onBindViewHolder(holder: ImageHolder, item: ImageItem) {
        //③给item控件赋值
        holder.image.setImageResource(item.resId)
    }

    override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ImageHolder {
        //②创建的时候给ImageHolder赋值布局
       return ImageHolder(inflater.inflate(R.layout.item_image,parent,false))
    }
}
/**
* Author: yangtianfu
* Date: 2021/1/28 20:39
* Describe:ViewDelegate是ItemViewDelegate子类
 * 用以自定义view做item的数据绑定方式
*/
class RichViewDelegate :ViewDelegate<RichItem,RichView>() {
    override fun onBindView(view: RichView, item: RichItem) {
        view.imageView.setImageResource(item.imageResId)
        view.textView.text = item.text
    }

    override fun onCreateView(context: Context): RichView {
        return RichView(context).apply {
            layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT) }
    }
}
class RichView(context: Context):LinearLayout(context) {
    val imageView = AppCompatImageView(context).apply {
        addView(this, LayoutParams(72.dp, 72.dp))
    }

    val textView = AppCompatTextView(context).apply {
        setTextColor(Color.BLACK)
        addView(this, LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT
        ))
    }

    init {
        orientation = VERTICAL
        gravity = Gravity.CENTER
        setPadding(16.dp, 16.dp, 16.dp, 16.dp)
    }
}
//顶层工具类,数字转dp
val Number.dp: Int get() = (toInt() * Resources.getSystem().displayMetrics.density).toInt()

多类型item数据注册显示

private fun imageAndTextItemsShow() {
        //注册ItemViewBinder,每一种数据源对应一种viewBinder,都需要进行注册
        adapter.register(ImageItemViewBinder())
        adapter.register(TextItemViewBinder())
        adapter.register(RichViewDelegate())
        binding.recycleView.adapter = adapter
        //多类型数据源
        val imageItem = ImageItem(R.mipmap.ic_launcher)
        val textItem = TextItem("world")
        val richItem = RichItem("自定义view做item", R.drawable.img_11)
        for (i in 0..19) {
            items.add(textItem)
            items.add(imageItem)
            items.add(richItem)
        }
        adapter.items = items //adapter会给items中每一种类型的itemData找到对应的itemViewBinder进行数据绑定
        adapter.notifyDataSetChanged()
    }

效果图

MultiTypeAdapter

包含GridView复杂视图使用

灵活配置每一行的列数
主要在于列数的动态设置,如下

  layoutManager.spanSizeLookup = object :GridLayoutManager.SpanSizeLookup(){
            override fun getSpanSize(position: Int): Int {
                //当前位置的 item 跨度大小,(SPAN_COUNT ÷ 此处返回值)== 该item所在行一共有几列
                //此处Category所在行要求只显示一列作为标题,所以返回计算公式如下
                return if (items[position] is Category) SPAN_COUNT else 1
            }

        }

在这里插入图片描述

  1. 标题行item
class Category(var title: String)
class CategoryItemViewBinder:ItemViewBinder<Category,CategoryItemViewBinder.ViewHolder>() {


    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val title: TextView = itemView.findViewById(R.id.title)
    }

    override fun onBindViewHolder(holder: ViewHolder, item: Category) {
        holder.title.text = item.title
    }

    override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder {
       return ViewHolder(inflater.inflate(R.layout.item_category,parent,false))
    }
}
  1. GridView的item
class Square(val number: Int) {
  var isSelected: Boolean = false
}
class SquareViewBinder(val selectedSet: MutableSet<Int>):ItemViewBinder<Square,SquareViewBinder.ViewHolder>() {

    override fun onBindViewHolder(holder: ViewHolder, item: Square) {
        holder.square = item
        holder.squareView.text = item.number.toString()
        holder.squareView.isSelected = item.isSelected
    }

    override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder {
        return ViewHolder(inflater.inflate(R.layout.item_square,parent,false))
    }

    inner class ViewHolder(itemView: View):RecyclerView.ViewHolder(itemView){
        val squareView: TextView = itemView.findViewById(R.id.square)
        lateinit var square: Square
        init {
            itemView.setOnClickListener {
                square.apply {
                    squareView.isSelected = !isSelected
                    this.isSelected = !isSelected
                }
                if (square.isSelected){
                    selectedSet.add(square.number)
                }else{
                    selectedSet.remove(square.number)
                }
            }
        }
    }

}

复杂页面实现

class MultiSelectableActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMultiSelectableBinding
    private val adapter = MultiTypeAdapter()
    private val items = ArrayList<Any>()
    private lateinit var fab: Button
    private lateinit var selectedSet: TreeSet<Int>//内部元素排序

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this,R.layout.activity_multi_selectable)
        initRecycle()
    }

    private fun initRecycle() {
        val layoutManager = GridLayoutManager(this, SPAN_COUNT)
        layoutManager.spanSizeLookup = object :GridLayoutManager.SpanSizeLookup(){
            override fun getSpanSize(position: Int): Int {
                //当前位置的 item 跨度大小,(SPAN_COUNT ÷ 此处返回值)== 该item所在行一共有几列
                //此处Category所在行要求只显示一列作为标题,所以返回计算公式如下
                return if (items[position] is Category) SPAN_COUNT else 1
            }

        }
        selectedSet = TreeSet()
        binding.recycleView.layoutManager = layoutManager
        adapter.register(CategoryItemViewBinder())
        adapter.register(SquareViewBinder(selectedSet))
        loadData()
        binding.recycleView.adapter = adapter

        binding.fab.setOnClickListener { v ->
            val content = StringBuilder()
            for (number in selectedSet) {
                content.append(number).append(" ")
            }
            Toast.makeText(v.context, "Selected items: $content", Toast.LENGTH_SHORT).show()
        }
    }

    private fun loadData() {
        val spacialCategory = Category("特别篇")
        items.add(spacialCategory)
        for (i in 0..6) {
            items.add(Square(i + 1))
        }
        val currentCategory = Category("本篇")
        items.add(currentCategory)
        for (i in 0..999) {
            items.add(Square(i + 1))
        }
        adapter.items = items
        adapter.notifyDataSetChanged()
    }

    companion object{
       private const val SPAN_COUNT = 5//每行排列 item 个数,在GridLayoutManager对象创建时需要传入
    }

}

源码地址

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值