美文网首页AndroidUI
RecyclerView的基础篇-列表的展示

RecyclerView的基础篇-列表的展示

作者: dashingqi | 来源:发表于2020-08-30 12:10 被阅读0次
Android_Banner.jpg

简介

  • 在本篇文章中,我将介绍使用RecyclerView展示一个列表数据
  • 展示的过程中,将介绍使用常规的写法,配合DataBinding的写法,以及使用第三方框架(binding-collection-adapter
  • 这三种写法最终展示的效果如下图所示


    UI展示

使用

  • 本文中使用网络图片,需要添加Glide
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
  • 同时别忘了给权限
<uses-permission android:name="android.permission.INTERNET" />
  • 本文中使用的网络图片地址
//准备的图片地址
    private var imgUrls = mutableListOf(
        "https://img.fulaishiji.com/images/goods/19883/big/03957c4d-6869-4cef-ad3e-824852f9da2b_800x800.png",
        "https://img.fulaishiji.com/images/goods/19307/big/e2635d9a-d5eb-4acf-b08c-44483a8554e2_800x800.jpg",
        "https://img.fulaishiji.com/images/goods/17249/big/3389ee28-5b14-4b55-8f5d-64a00d5deb37_800x800.jpg",
        "https://img.fulaishiji.com/images/goods/12461/big/b0be6cbd-3164-470b-b355-d9b41b0ce0e6_800x800.jpg",
        "https://img.fulaishiji.com/images/goods/16743/big/65843bf9-7ba1-48d0-884e-aefeebf9b635_964x964.jpg",
        "https://img.fulaishiji.com/images/goods/12534/big/3dabd1b5-f37f-4320-9832-fccbdc8e1ecf_800x800.jpg",
        "https://img.fulaishiji.com/images/goods/14897/big/e1c774c9-7003-4a4a-9e12-1bdf1d729457_800x800.jpg",
        "https://img.fulaishiji.com/images/goods/12753/middle/95f064c5-8bfc-44d6-a7d8-5058cb3f93e7_800x800.jpg",
        "https://img.fulaishiji.com/images/goods/10396/middle/e174305c-b32c-4b9d-a0b1-a14de0ae011a_3648x3648.jpg",
        "https://img.fulaishiji.com/images/goods/17824/middle/19d6253c-1ef9-48f0-b63f-d01d2f354eee_2728x2728.jpg",
        "https://img.fulaishiji.com/images/goods/17337/middle/9974521a-34c4-4daf-9c07-717dface9cce_800x800.jpg",
        "https://img.fulaishiji.com/images/goods/13223/middle/a0a32982-3af5-45ae-9252-a554503ec829_800x800.jpg",
        "https://img.fulaishiji.com/images/goods/19906/middle/41dca304-69b2-490f-b001-0b99fa4fcc60_1008x1008.jpg",
        "https://img.fulaishiji.com/images/goods/19653/middle/c31cca8b-7468-4d5f-a06c-8427a7d01656_800x800.jpg"
    )
常规写法
  • activity_main.xml布局文件
  <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".GridLayoutManagerActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
  • MainActivity中的代码
  class GridLayoutManagerActivity : AppCompatActivity() {
    private val items = ArrayList<Food>()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_grid_layout_manager)

        for (index in 0 until 200) {
            val food = Food()
            food.name = "name $index"
            food.desc = "desc $index"
            val position = (Math.random() * (imgUrls.size - 1)).toInt()
            food.imgUrl = imgUrls[position]
            items.add(food)
        }
        var adapter = GridLayoutAdapter(items)
        rv.adapter = adapter
    }
}
  • Adapter中的代码
class GridLayoutAdapter(var foodList: ArrayList<Food> = ArrayList()) :
    RecyclerView.Adapter<GridLayoutAdapter.MyViewHolder>() {


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        var itemView = View.inflate(parent.context, R.layout.grid_layout_item, null)
        return MyViewHolder(itemView)
    }

    override fun getItemCount(): Int {
        return foodList.size
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.itemView.tvName.text = foodList[position].name
        holder.itemView.tvDesc.text = foodList[position].desc
        Glide.with(holder.itemView.context).load(foodList[position].imgUrl)
            .into(holder.itemView.ivCover)
    }

    class MyViewHolder(item: View) : RecyclerView.ViewHolder(item) {
    }
}
  • item的布局文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

        <ImageView
            android:id="@+id/ivCover"
            android:layout_width="80dp"
            android:scaleType="centerCrop"
            android:layout_height="80dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/tvName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_marginTop="4dp"
            android:gravity="center"
            android:textColor="#333333"
            android:textSize="18sp"
            app:layout_constraintLeft_toRightOf="@+id/ivCover"
            app:layout_constraintTop_toTopOf="@+id/ivCover" />

        <TextView
            android:id="@+id/tvDesc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:textSize="14sp"
            app:layout_constraintBottom_toBottomOf="@+id/ivCover"
            app:layout_constraintLeft_toLeftOf="@+id/tvName"
            app:layout_constraintTop_toBottomOf="@+id/tvName" />

</androidx.constraintlayout.widget.ConstraintLayout>
配合DataBinding使用
  • 启用DataBinding
// 在module 的 build.gradle中的 android{}中加入如下
  dataBinding{
        enabled true
    }
  • Activity的布局文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
         />
</androidx.constraintlayout.widget.ConstraintLayout>
  • Item的布局文件
<?xml version="1.0" encoding="utf-8"?>
<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.dashingqi.module.recyclerview.Food" />

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/ivCover"
            // 这里使用了DataBinding中自定义属性功能
            // 自定义属性的文件在下文中也给出了
            imageUrl="@{item.imgUrl}"
            android:layout_width="80dp"
            android:scaleType="centerCrop"
            android:layout_height="80dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/tvName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_marginTop="4dp"
            android:gravity="center"
            android:text="@{item.name}"
            android:textColor="#333333"
            android:textSize="18sp"
            app:layout_constraintLeft_toRightOf="@+id/ivCover"
            app:layout_constraintTop_toTopOf="@+id/ivCover" />

        <TextView
            android:id="@+id/tvDesc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:text="@{item.desc}"
            android:textSize="14sp"
            app:layout_constraintBottom_toBottomOf="@+id/ivCover"
            app:layout_constraintLeft_toLeftOf="@+id/tvName"
            app:layout_constraintTop_toBottomOf="@+id/tvName" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
  • Activity中的代码
class MainActivity : AppCompatActivity() {

    private var data = ArrayList<Food>()
   
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        configRv()
    }

    private fun configRv() {
        for (index in 0 until 200) {
            val food = Food()
            food.name = "name $index"
            food.desc = "desc $index"
            val position = (Math.random() * (imgUrls.size - 1)).toInt()
            food.imgUrl = imgUrls[position]
            data.add(food)
        }
        // RecyclerView中 可以通过在xml文件中指定LayoutManager,这里我们是通过代码形式的
        rv.layoutManager = LinearLayoutManager(this)
        rv.adapter = RVAdapter(data)
    }
}
  • xml文件中指定RecyclerView的LayoutManager
 <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            // xml文件中指定的LayoutManager
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

    </androidx.constraintlayout.widget.ConstraintLayout>
  • Adapter中的代码
class RVAdapter(var itemData: ArrayList<Food> = ArrayList()) :
    RecyclerView.Adapter<RVAdapter.MyViewHolder>() {
    private val TAG = "RVAdapter"
    private var viewHolderCount = 0


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        viewHolderCount++
        Log.d(TAG, "onCreateViewHolder ---->$viewHolderCount")
        var itemBinding = DataBindingUtil.inflate<RvItemBinding>(
            LayoutInflater.from(parent.context),
            R.layout.rv_item,
            parent,
            false
        )
        return MyViewHolder(itemBinding)
    }

    override fun getItemCount(): Int {
        return itemData.size
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        Log.d(TAG, ": onBindViewHolder")
        var dataBinding = holder.viewDataBinding as RvItemBinding
        dataBinding.item = itemData[position]
        dataBinding.executePendingBindings()
    }

    class MyViewHolder(var viewDataBinding: ViewDataBinding) :
        RecyclerView.ViewHolder(viewDataBinding.root) {

    }

    companion object {
        private const val TAG = "RVAdapter"
    }
}
  • 自定义属性文件
object ImageBindAdapter {

   @JvmStatic
   @BindingAdapter(value = ["imageUrl"], requireAll = false)
    fun setImageUrl(view: ImageView, url: String) {
       Glide.with(view.context).load(url).into(view)
   }
}
binding-collection-adapter+DataBinding+ViewModel
  • 上文中介绍的是把数据放到了Activity中,这回我们把数据交给ViewModel来处理
  • binding-collection-adapter 框架可以帮我们省去写繁琐Adapter的过程
  • 这里仅仅介绍使用方法,不做原理的介绍
引入binding-collection-adapter
implementation 'me.tatarka.bindingcollectionadapter2:bindingcollectionadapter:4.0.0'
implementation 'me.tatarka.bindingcollectionadapter2:bindingcollectionadapter-recyclerview:4.0.0'
  • MainActivity中的代码
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val mainBinding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        //绑定ViewModel
          val foodViewModel = ViewModelProvider(this)[FoodViewModel::class.java]
        mainBinding.viewModel = foodViewModel
    }

}
  • activity_main 文件
<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>

        <variable
            name="viewModel"
            type="com.dashingqi.module.recyclerview.FoodViewModel" />

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv"
            itemBinding="@{viewModel.itemBinding}"
            items="@{viewModel.items}"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
  • ViewModel文件
class FoodViewModel: ViewModel() {
    val items = ObservableArrayList<Food>()
    val itemBinding = ItemBinding.of<Food>(BR.item, R.layout.rv_item)
    init {
        for (index in 0 until 200) {
            val food = Food()
            food.name = "name $index"
            food.desc = "desc $index"
            val position = (Math.random() * (imgUrls.size - 1)).toInt()
            food.imgUrl = imgUrls[position]
            items.add(food)
        }
    }
}
  • 由于Item和自定义属性的文件都是一样的,这里就不贴出了,可参照上文

总结

针对上述写法做一个对比
  • 常规写法,需要自己写Adapter,需要在onBindViewHolder中设置数据
  • DataBinding,同样需要写Adapter,相比较于常规写法,不需要在onBindViewHolder中设置每一个控件的数据数据,是把携带数据的Bean与item的xml文件进行绑定,数据设置的过程是在xml文件中设置的
  • binding-collection-adapter:不用写Adapter,一切数据的设置显示都是在xml文件中操作,更符合数据驱动UI的显示,绑定数据源,数据源发生变化,UI自动显示。
实际项目
  • 在目前的项目中,我们开始使用binding-collection-adapter 在mvvm框架中,配合kotlin,代码更加简洁,但是使用过程中也发现了,如果xml中有错误的写法,在编译的时候,会报错,但是错误的指明不是很直观,这也是目前我是用它发现的不足之处吧。

相关文章

网友评论

    本文标题:RecyclerView的基础篇-列表的展示

    本文链接:https://www.haomeiwen.com/subject/felfvctx.html