本文正在参与「金石计划 . 分割6万现金大奖」

前言

接触到BRVAH是由于咱们开发项目中就用到它的BaseQuickAdapter,写Adapter确实好用,不过一看版本是2x的,所以去结构发布Github官方代码库(CymChad/BaseRecyclerViewAdapterHelper: BRVAH:Powerful and flexible RecyclerAdapter (github.com))学习了一下最新的4x版本。下面咱们就来看看最新的BRVAH如何运用。

正篇

概况

BRVAH (recyclerview.org)是能够在Android中更便利完成Adapter的RecyclerAdapter结构,它的引进能够节约许多开发时间,

强大而灵活的RecyclerView Adapter——BRVAH(框架引入与BaseQuickAdapter使用篇)
它封装了许多好用的功用,如:点击事情、数据操作、动画、空视图,加载更多等
强大而灵活的RecyclerView Adapter——BRVAH(框架引入与BaseQuickAdapter使用篇)

引进结构

目前该结构现已更新到4x版本,但需求留意这个结构从2x到3x现已不完全兼容,而3x到4x也是不兼容的,但4x有着前几代都没有的优势,它引进更简单,由于v4版本现已上传 maven 中央库房,不需求再引进三方库房装备了。

implementation "io.github.cymchad:BaseRecyclerViewAdapterHelper:4.0.0-beta04"

直接引进到app的build.gradle文件中的dependencies依靠中即可

强大而灵活的RecyclerView Adapter——BRVAH(框架引入与BaseQuickAdapter使用篇)

BaseQuickAdapter的运用

本文章Demo更新在AndroidStudyman/StudyDemo: Study demo (github.com)项目中,运用办法参考文档:BaseQuickAdapter CymChad/BaseRecyclerViewAdapterHelper Wiki (github.com)

引进结构后,咱们能够看到BaseQuickAdapter源码如下:

强大而灵活的RecyclerView Adapter——BRVAH(框架引入与BaseQuickAdapter使用篇)

class AdapterDemo : BaseQuickAdapter<ModelDemoItem, AdapterDemo.VH>() {
    //自定义ViewHolder类
    class VH(
        parent: ViewGroup,
        binding: LayoutPageAdapterDemoBinding = LayoutPageAdapterDemoBinding.inflate(
            LayoutInflater.from(parent.context), parent, false
        )
    ) : RecyclerView.ViewHolder(binding.root)
    override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
        // 回来一个 ViewHolder
        return VH(parent)
    }
    override fun onBindViewHolder(holder: VH, position: Int, item: ModelDemoItem?) {
        // 设置item数据
        item?.getItemName()
    }
}

上面的代码已更新到(StudyDemo/AdapterDemo.kt at main AndroidStudyman/StudyDemo (github.com))中

咱们创立一个AdapterDemo类去继承BaseQuickAdapter,然后写一个VH类去绑定Viewbinding,能够自定义item数据类型,当然,也能够不必我的比如,运用官方教程中不必ViewBinding的方式去写,运用QuickViewHolder:

class TestAdapter : BaseQuickAdapter<Status, QuickViewHolder>() {
    override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): QuickViewHolder {
        // 回来一个 ViewHolder
        return QuickViewHolder(R.layout.layout_animation, parent)
    }
    override fun onBindViewHolder(holder: QuickViewHolder, position: Int, item: Status?) {
        // 设置item数据
    }
}

其间adapter中对应的VH中binding的布局如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    app:cardCornerRadius="12dp"
    app:cardElevation="2dp"
    app:cardPreventCornerOverlap="false"
    android:layout_margin="8dp"
    android:orientation="vertical">
    <TextView
        android:id="@+id/vName"
        android:layout_width="60dp"
        android:layout_height="wrap_content"
       android:layout_gravity="center|top"
        android:gravity="center"
        android:layout_marginTop="10dp"
        android:text="@string/name"/>
    <TextView
        android:id="@+id/vAge"
        android:layout_width="60dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:gravity="center"
        android:layout_gravity="center"
        android:text="@string/age"/>
</androidx.cardview.widget.CardView>

接下来,这是我写的item列表的属性类:

class ModelDemoItem(
    var name : String? = null,
    var age: Int) {
    fun getItemName() : String? {
        return name
    }
    fun getItemAge() : Int {
        return age
    }
}

用于设置item列表每项展示的数据:

override fun onBindViewHolder(holder: VH, position: Int, item: ModelDemoItem?) {
    // 设置item数据
    val name = holder.itemView.findViewById<TextView>(R.id.vName)
    if (item != null) {
        name.text = item.name
    }
    val age = holder.itemView.findViewById<TextView>(R.id.vAge)
    if (item != null) {
        age.text = item.age.toString()
    }
}

咱们在每项中增加了name和age数据,然后在adapter中onBindViewHolder()办法设置到对应布局中:

class ActivityAdapterDemo : AppCompatActivity() {
    val tag : String = "ActivityAdapterDemo"
    private lateinit var binding : ActivityAdapterDemoBinding
    private lateinit var adapterDemo: AdapterDemo
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityAdapterDemoBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.vAdapterList.layoutManager = LinearLayoutManager(this)
        val itemBeans: ArrayList<ModelDemoItem> = ArrayList()
        for (i in 0..49) {
            itemBeans.add(ModelDemoItem("Name $i", i))
        }
        adapterDemo = AdapterDemo()
        adapterDemo.submitList(itemBeans)
        binding.vAdapterList.adapter = adapterDemo
        ...
        }
  }

上面的代码就是在咱们在RecyclerView中绑定Aapter,并往里边填充数据,咱们调用BaseQuickAdapter的submitList办法填充数据,RecyclerView地点的Activity视图布局如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ActivityAdapterDemo"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:orientation="horizontal"
        android:baselineAligned="false">
        <LinearLayout
            android:layout_width="56dp"
            android:layout_height="56dp"
            android:orientation="horizontal"
            android:gravity="center">
            <androidx.appcompat.widget.AppCompatImageView
                android:layout_width="16dp"
                android:layout_height="match_parent"
                android:src="@drawable/vector_arrow_back"
                android:scaleType="fitCenter"
                android:visibility="invisible"/>
        </LinearLayout>
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="56dp"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="horizontal">
            <androidx.appcompat.widget.AppCompatTextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textStyle="bold"
                android:text="@string/adapter_demo"
                android:textColor="@color/black"
                android:textSize="16sp"
                app:autoSizeMaxTextSize="16sp"
                app:autoSizeMinTextSize="8dp"
                app:autoSizeTextType="uniform"
                android:maxLines="1"/>
        </LinearLayout>
        <LinearLayout
            android:layout_width="56dp"
            android:layout_height="56dp">
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="@string/next"
                android:visibility="invisible"/>
        </LinearLayout>
    </LinearLayout>
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/vAdapterList"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

咱们能够在Activity中的adapter实例化目标中调用相应的点击事情,长按事情以及为子项也增加对应的事情监听:

//item 长按事情
adapterDemo.setOnItemLongClickListener { adapter, view, position ->
    Log.i(tag, " setOnItemLongClickListener ${adapter.hashCode()}, ${view.tag}, $position ")
    val intent = Intent(this, MainActivity ::class.java)
    intent.action = Intent.ACTION_VIEW
    true
}
//item 子控件点击事情
// 需求传递控件 id
adapterDemo.addOnItemChildClickListener(R.id.vName) { adapter, view, position ->
    Log.i(tag, " addOnItemChildClickListener ${adapter.hashCode()}, ${view.tag}, $position ")
    Toasty.info(
        baseContext,
        "addOnItemChildClickListener!").show()
}
//item 子控件长按事情
// 需求传递控件 id
adapterDemo.addOnItemChildLongClickListener(R.id.vAge) {  adapter, view, position ->
    Log.i(tag, " addOnItemChildLongClickListener ${adapter.hashCode()}, ${view.tag}, $position ")
    Toasty.info(
        baseContext,
        "addOnItemChildLongClickListener!!").show()
    true
}

除了设置数据集合的submitList()办法,还有许多十分便利的数据设置办法,如下:

修正某一位置的数据
val item = ModelDemoItem("Name 6", 6)
//修正index为1处的数据
adapterDemo[1] = item
新增数据
// 尾部新增数据
adapterDemo.add(item)
// 在指定位置增加一条新数据
adapterDemo.add(1, item)
//val itemBeans: ArrayList<ModelDemoItem> = ArrayList()
// 增加数据集
adapterDemo.addAll(itemBeans)
// 指定位置增加数据集
adapterDemo.addAll(1, itemBeans)
删去数据
// 删去数据
adapterDemo.remove(item)
// 删去指定位置数据
adapterDemo.removeAt(1)
交流数据位置
// 交流两个位置的数据
adapterDemo.swap(1, 3)
获取Item数据的索引
// 如果回来 -1,表明不存在
adapterDemo.getItemPosition(item)
根据索引,获取Item数据
// 如果回来 null,表明没有数据
adapterDemo.getItem(1)

以上都是数据的操作办法,按需所取就行,但要留意一些办法中数据操作的合理性,防止出现存取过界的情况产生

增加动画

内置了五种动画:

/**
 * BaseQuickAdapter.AnimationType.AlphaIn
 * BaseQuickAdapter.AnimationType.ScaleIn
 * BaseQuickAdapter.AnimationType.SlideInBottom
 * BaseQuickAdapter.AnimationType.SlideInLeft
 * BaseQuickAdapter.AnimationType.SlideInRight
 */
adapterDemo.setItemAnimation(BaseQuickAdapter.AnimationType.AlphaIn)

源码如下:

强大而灵活的RecyclerView Adapter——BRVAH(框架引入与BaseQuickAdapter使用篇)
AlphaIn:淡入项目的动画,在默许 300 毫秒内以统一速率将 alpha 从默许 0f 更改为 1.0f
ScaleIn:缩放项目的动画,在默许 300 毫秒内将项目的 scaleX 和 scaleY 从默许的 0.5f 更改为 1.0f。(运用具有默许因子的 DecelerateInterpolator)
SlideInBottom:让项目从底部滑入的动画。(运用因子为 1.3 的 DecelerateInterpolator)默许持续时间为 400 毫秒。
SlideInLeft:让项目从左边滑入的动画。(运用因子为 1.8 的 DecelerateInterpolator)默许持续时间为 400 毫秒。
SlideInRight:让项目从右侧滑入的动画。(运用因子为 1.8 的 DecelerateInterpolator)默许持续时间为 400 毫秒。
其间DecelerateInterpolator是一种减速插值器,是一种变化率从很快变慢的插值器。

官方也明确举例能够自定义动画:

class CustomAnimation1 : ItemAnimator {
    override fun animator(view: View): Animator {
        // 创立三个动画
        val alpha: Animator = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f)
        val scaleY: Animator = ObjectAnimator.ofFloat(view, "scaleY", 1.3f, 1f)
        val scaleX: Animator = ObjectAnimator.ofFloat(view, "scaleX", 1.3f, 1f)
        scaleY.interpolator = DecelerateInterpolator()
        scaleX.interpolator = DecelerateInterpolator()
        // 多个动画组合,能够运用 AnimatorSet 包装
        val animatorSet = AnimatorSet()
        animatorSet.duration = 350
        animatorSet.play(alpha).with(scaleX).with(scaleY)
        return animatorSet
    }
}
// 设置动画
adapter.itemAnimation = CustomAnimation1()

然后在Adapter类中设置startItemAnimator办法来重写动画履行操作:

  override fun startItemAnimator(anim: Animator, holder: RecyclerView.ViewHolder) {
    }

还有用于决定是否启用动画的办法:

adapterDemo.animationEnable = true

加载空布局

空视图只会在无数据的情况下显现

  • 运用Layout Id

    adapterDemo.setEmptyViewLayout(context, R.layout.loading_view)
    
  • 运用View

    mAdapter.emptyView = view
    
  • 留意:如果你运用GridLayoutManager,请切换至QuickGridLayoutManager,否则空布局无法铺满

还有更多用法能够检查官方文档:BaseQuickAdapter CymChad/BaseRecyclerViewAdapterHelper Wiki GitHub,现已写的很详细了,仅仅没有完好的Demo,本文就是依据此文档完成的Demo。

总结

由于工作中开发经常运用到,但没想到内容这么多,这仅仅最常用的部分,此外,还有多类型布局Adapter等等

强大而灵活的RecyclerView Adapter——BRVAH(框架引入与BaseQuickAdapter使用篇)
不过官方的4x版本也在持续写文档,敬请期待吧,当然也能够去看看官方提供的Demo:BRVAH-v4.apk – 蓝奏云 (lanzouj.com),不过不知道为什么是个APK使用,不是项目形式。