Linx's Solution

Android Kotlin RecyclerView Databinding

kotlinandroid

Android Kotlin RecyclerView Databinding

  • 一个已经能进行简单的按按钮操作的RecyclerView项目

    apply plugin: 'kotlin-android'
    apply plugin: 'kotlin-kapt'
    apply plugin: 'kotlin-android-extensions'
    
    dataBinding {
        enabled = true
    }
    
  • 一个数据绑定辅助类

    /**
      * 赋予ImageView和ImageButton以 app:img 的属性, 用于绑定图片
      * @author linx
      */
    class BindingUtil {
        companion object {
            @BindingAdapter("app:img")
            @JvmStatic
            fun imgData(iv: ImageView, data: Int) {
                iv.setImageResource(data)
            }

            @BindingAdapter("app:img")
            @JvmStatic
            fun imgData(iv: ImageButton, data: Int) {
                iv.setBackgroundResource(android.R.color.transparent)
                iv.setImageResource(data)
            }

            /**
             * ImageView绑定图片地址url
             * @param iv ImageView
             * @param data Any
             */
            @BindingAdapter("app:url")
            @JvmStatic
            fun ImageViewUrl(iv: ImageView, data: Any) {
                if (data is String) {
                    if (!data.isEmpty()) {
                        Glide.with(iv.context).load(Uri.parse(data)).into(iv)
                    } else {
                        iv.setImageResource(R.mipmap.touxiang2_default)
                    }

                } else if (data is Int) {
                    iv.setImageResource(data)
                }
            }

        }
    }
  • 一个数据adapter
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import androidx.databinding.DataBindingUtil
    import androidx.databinding.ViewDataBinding
    import androidx.recyclerview.widget.RecyclerView.Adapter
    import androidx.recyclerview.widget.RecyclerView.ViewHolder
    import java.util.*

    /**
    * RecylerView 数据绑定适配器
    * @param T
    * @property mList List<T>?
    * @property layoutId Int?
    * @property brId Int?
    * @property itemClick ItemClick?
    * @constructor
    */
    open class QJBaseAdapter<T>(var mList: List<T>?, var layoutId: Int?, var brId: Int?) :
        Adapter<QJBaseAdapter.QJBaseViewHolder>() {


        var itemClick: ItemClick? = null

        /**
        * 更新数据
        * @param items ArrayList<T>?
        */
        fun updateData(items: ArrayList<T>?) {
            this.mList = items
            notifyDataSetChanged()
        }

        interface ItemClick {
            fun OnItemClick(v: View, position: Int)
        }

        fun setItemClickListener(itemClick: ItemClick) {
            this.itemClick = itemClick
        }


        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QJBaseViewHolder {

            val binding: ViewDataBinding = DataBindingUtil.inflate(
                LayoutInflater.from(parent.context), this.layoutId!!, parent, false
            )
            return QJBaseViewHolder(binding)
        }

        override fun getItemCount(): Int = mList!!.size

        override fun onBindViewHolder(holder: QJBaseViewHolder, position: Int) {
            var item = mList!![position] // 这里必须为var 因为需要修改列表的数据
            doBeforeShow(holder.binding.root, item)
            holder.binding.setVariable(this.brId!!, mList!![position])
            holder.binding.executePendingBindings()
            // item点击事件
            holder.binding.root.setOnClickListener {
                itemClick?.OnItemClick(holder.binding.root, position)
            }
            forInnerControl(holder.binding.root, item)
        }


        /**
        * 在绑定数据之前执行, 可以修改数据 item 的值
        * @param itemView View?
        * @param item T
        */
        open fun doBeforeShow(itemView: View?, item: T) {

        }

        /**
        * 用于给ItemView内部的组件绑定事件
        * @param itemView View?
        * @param item T
        */
        open fun forInnerControl(itemView: View?, item: T) {

        }

        class QJBaseViewHolder(var binding: ViewDataBinding) : ViewHolder(binding.root)
    }
  • 声明数据类
    data class SchoolNews(
        val title: String = "", //标题
        val contentShort: String = "", //内容简写
        val date: String = "", //日期
        val state: Int = 0, //0 unreaded 1 readed 2 pinned
        var img:Int =0
    )
  • RecyclerView 的itemview中声明, 注意顶层元素使用layout, 其他盲敲即可, IDE会补全
    <layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="schoolNews"
            type="model.SchoolNews" />
    </data>
  • android:text="@{schoolNews.title}" 这样的方式绑定数据, ImageView和ImageButton则以 app:img="@{schoolNews.img}" img 是resId即资源id, 图片的值不能直接绑定, 需要用代码setImageResource来实现

  • 创建一个特定的适配器, 重写两个方法

    class SchoolNewsAdapter(mList: List<SchoolNews>?, layoutId: Int?, brId: Int?) :
        QJBaseAdapter<SchoolNews>(mList, layoutId, brId) {

        override fun forInnerControl(itemView: View?, item: SchoolNews) {
            // 这里可以为每个 itemView 的子控件添加事件
            
        }

        // 在这个方法中对item进行修改是可以应用到界面的, 不过肯定不能执行过于耗时的操作
        override fun doBeforeShow(itemView: View?, item: SchoolNews) {
            // 根据state字段的值来决定显示哪张图片
            when (item.state) {
                0-> item.img = R.mipmap.yuandian
                2-> item.img = R.mipmap.schoolnews_guding
                else->item.img = R.color.mainwhite
            }
        }


    }

顺便说下, 代码中访问内置颜色是这样的android.R.color.transparent

  • 最后就是使用了
    var mAdapter: SchoolNewsAdapter? = null

    private fun initView() {
        mAdapter = SchoolNewsAdapter(
            schoolNews, R.layout.recycler_view_item_school_news_info,
            BR.schoolNews
        )

        schoolNewsRv.adapter = mAdapter
    }

这里说明下 schoolNews为 ArrayList<SchoolNews>, R.layout.recycler_view_item_school_news_info为item的布局文件, BR.schoolNews这个就是前面 <layout>内的variable了, 如果最开始的插件没有配置好, 这个会报错. 原理其实就是编译器自动帮我们生成好了代码

更新数据则使用 mAdapter!!.updateData(schoolNews)即可