当前位置:首页 > 技术文章 > 正文内容

使用Databinding为Recyclerview使用同一个ViewHolder加载不同Item

u3blog2年前 (2022-10-08)技术文章562

提示:在阅读本篇文章前,你最好对android databinding有一定了解,本文使用的代码均为kotlin,但是不用担心,都很简单

最近在写项目的时候使用了databinding技术,突发奇想,databinding是不是也能应用于recyclerview中,让加载多个不同的item更简单呢。在网上搜索过后读到了<a href="https://medium.com/androiddevelopers/android-data-binding-recyclerview-db7c40d9f0e4?">这篇文章</a>,发现作者巧妙的使用了原有的函数来实现,在结合了自身使用实践之后,就有了现在这篇博客。

提问环节

为了让阅读过程更有目的性,先提几个问题,让大家带着问题来阅读
<strong>
adaper部分

Q1:onCreateViewHolder中怎么动态的创建不同类型的ViewHolder

Q2:onBindViewHolder中怎么绑定不同类型的对象到viewHolder里

viewholder部分

Q3:怎么把传过来的不同类型对象绑定到layout上面
</strong>

我们先来看看base adapter的代码


abstract class BaseAdapter : RecyclerView.Adapter<MyViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val binding = DataBindingUtil.inflate&lt;ViewDataBinding&gt;(
            layoutInflater,viewType,parent,false
        )
        return MyViewHolder(binding)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val obj = getObjForPosition(position)
        holder.bind(obj)
    }

    override fun getItemViewType(position: Int): Int {
        return getLayoutIdForPosition(position)
    }
    abstract fun getObjForPosition(pos:Int):Any?
    abstract fun getLayoutIdForPosition(pos:Int):Int
    }

在<strong>onCreateViewHolder函数里</strong>我们首先初始化了一个layoutInflater,再使用DataBindingUtil创建了一个viewDatabinding对象,<strong>注意到这里的第二个参数很奇怪,居然是viewType</strong>,这是为什么呢,我们接下来会分析到。在函数的最后,我们创建了一个viewholder并返回,构造参数传入了我们创建的ViewDatabinding对象。

接下来,是<strong>onBindViewHolder函数</strong>,我们首先使用<strong>接口getObjForPosition(position)</strong>获取到了一个类型为any?的对象,在kotlin里面,any基本可以等同于java中的object,并把这个对象通过viewholder的bind方法与viewholder进行了绑定操作

<strong>重写了getItemViewType方法</strong>,在这个方法里,我们通过另一个<strong>接口getLayoutIdForPosition获取到了一个类型为Int的返回值</strong>,通过这个函数,我们可以看出,这种使用方法<strong>把layout id当作了itemviewtype,在一个Int对象中传递了两个信息</strong>,同时由于layout id不会重复,又保证了itemtype的唯一性。

<strong>总结1:
到了这里,我们可以回答前两个问题了

Q1:onCreateViewHolder中怎么动态的创建不同类型的ViewHolder

并没有创建不同类型的viewholder,只使用了一个MyViewHolder

Q2:onBindViewHolder中怎么绑定不同类型的对象到viewHolder里

通过抽象接口getLayoutIdForPosition将layout id作为返回值在getItemViewType中返回,再在onBindViewHolder中使用抽象接口getObjForPosition获得类型为Any?的对象并绑定到viewholder实现了将不同类型的对象绑定到viewHolder中
</strong>

我们来看一个具体的adapter例子

class MyAdapter(layouts:List&lt;Int&gt;)
    : MultipleLayoutAdapter(layouts){
    var myData:List&lt;Moment&gt;? = null
    var header: UserInfo? = null
    override fun getObjForPosition(position: Int): Any? {
        return if (position == 0)
            header
        else
            myData?.get(position - 1)
    }

    override fun getItemCount(): Int {
        return if (myData== null)
            1
        else
            myData?.size!! + 1
    }
}

实现了上面提到的两个抽象接口,一个返回了对象的数量,一个返回了根据位置计算的layout id

<strong>viewholder部分</strong>

通过上述的过程,我们又会产生新的疑问,并没有使用多个viewholder来适配多个item,那是怎么使用同一个viewholder来搭配不同的layout的呢?内部又是怎么绑定的呢?

先来看一个viewHolder的代码

 class MyViewHolder(private val binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root) {
    private val context: Context = binding.root.context
    fun bind(obj:Any?){
        binding.setVariable(BR.obj,obj)
        binding.executePendingBindings()
    }

}

我们看到<strong>构造参数,是一个ViewDatabinding类型</strong>,在<strong>bind函数里,使用viewdatabinding对象的setVariable传入了参数</strong>obj,并且调用了<strong>executePendingBindings进行刷新</strong>,这样,我们的界面上就可以访问这个obj类了。

看一个layout的例子

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="obj"
            type="com.u3coding.wemoment_03_12.data.entity.UserInfo" />
    </data>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="@dimen/header_view_height">

        <ImageView
            android:id="@+id/background_iv"
            android:layout_width="match_parent"
            android:layout_height="@dimen/header_background_image_height"
            android:scaleType="centerCrop" />

        <TextView
            android:id="@+id/name_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_marginEnd="@dimen/header_avatar_margin_right"
            android:layout_marginBottom="@dimen/header_avatar_margin_bottom"
            android:text="@{obj.username}"
            android:textColor="@color/header_name_color"
            android:textSize="@dimen/name_size" />

        <ImageView
            android:id="@+id/avatar_iv"
            android:layout_width="@dimen/header_avatar_size"
            android:layout_height="54dp"
            android:layout_gravity="bottom|end"
            android:layout_margin="@dimen/avatar_magrin" />
    </FrameLayout>
</layout>

<strong>Q&A

现在我们可以回答全部的问题了
Q1:onCreateViewHolder中怎么动态的创建不同类型的ViewHolder

并没有创建不同类型的viewholder,只使用了一个MyViewHolder

Q2:onBindViewHolder中怎么绑定不同类型的对象到viewHolder里

通过抽象接口getLayoutIdForPosition将layout id作为返回值在getItemViewType中返回,再在onBindViewHolder中使用抽象接口getObjForPosition获得类型为Any?的对象并绑定到viewholder实现了将不同类型的对象绑定到viewHolder中

Q3:怎么把传过来的不同类型对象绑定到layout上面

使用viewdatabinding的setVariable方法直接绑定到具体的变量上,再在布局中使用的时候指定类型,直接使用即可

此套方法巧妙的利用了databinding的特点,给adapter中itemtype赋予更多的含义,来实现了同一个viewholder对应不同布局的目的,十分的巧妙,尤其是给viewtype赋予更多含义,让人联想到了android view里面的measurespec,同样是给一个int赋予多重含义
</strong>

扫描二维码推送至手机访问。

版权声明:本文由u3blog发布,如需转载请注明出处。

本文链接:https://u3blog.xyz/?id=695

分享给朋友:

“使用Databinding为Recyclerview使用同一个ViewHolder加载不同Item” 的相关文章

开源数据库Postgresql安装/卸载总结

What Postgresql一个开源数据库,类似mysql,由于mysql被收购了,这个数据库正被越来越多的使用 怎么安装?安装非常简单,但是安装过后的初始化还是有点麻烦,具体可以看这篇文章为什么要卸载?安装好之后,如果你很倒霉的话,会遇到postgresql服务怎么都启动不了,输入psql指令显...

AndroidStudio插件开发——RemoveButterKnife从构思到实现

AndroidStudio插件开发——RemoveButterKnife从构思到实现

ReomveButterKnife插件这是一个用于移除代码中对ButterKnife使用的AS插件,接下来我们将从头开始讲讲AS插件开发和这个插件的开发过程地址是<a href="https://github.com/u3shadow/RemoveButterKnife"...

项目的改造——RemoveButterKnife插件代码的重构

项目的改造——RemoveButterKnife插件代码的重构

前言这篇文章记述了我的插件RemoveButterKnife的代码改进过程以及思路,关于插件,各位可以看RemoveButterKnife代码库,关于文章,可以看构思到实现RemoveButterKnife 原因近期想给原来的插件RemoveButterKnife加入一些新的功能,发现以前的代码没...

项目的升级-给RemoveButterKnife插件增加新功能

项目的升级-给RemoveButterKnife插件增加新功能

前言经过项目的初步编写和进一步改造,RemoveButterKnife插件终于也有模有样了,但是,功能上仅仅支持Activity/Fragment的BindView注解。 关于编写和优化的过程可以看下面两篇文章项目构造RemoveButterKnife 项目改进-重构RemoveButterKn...

Android测试体系-在MVVM架构中如何测试Model层与ViewModel层

背景此文章是对于google code lab中《Introduction to Test Double and Dependence injection》 与 《Testing Basics》的总结,本篇主要讲述如何在mvvm架构的android项目中对Model层以及ViewModel层进行测试...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。