美文网首页
【UI篇3】RecyclerView创建动态列表

【UI篇3】RecyclerView创建动态列表

作者: 农民工Alan | 来源:发表于2022-03-24 15:46 被阅读0次

    前言
    RecyclerView是一款功能十分强大的widget,涉及到列表呈现时,通常都会使用到RecyclerView,功能如此强大的widget不做一下记录实在可惜。

    1. 实现RecyclerView

    1.1 RecyclerView布局
    • 布局
    <androidx.constraintlayout.widget.ConstraintLayout 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="match_parent">
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="16dp"
            android:contentDescription="@string/fab_content_description"
            android:src="@drawable/ic_add_black_24dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    Activity中的设置

    • Activity
    //初始使用
    mRecyclerView = findViewById(R.id.recycler_view)
    mRecyclerView.layoutManager = LinearLayoutManager(this)
    mRecyclerView.adapter = mAdapter
    
    1.2 规划布局

    组成RecyclerView的表项可能有多种类型,不同的类型会有不同的布局。根据列表或网格的外观,确定布局管理器,RecyclerView中的列表项由LayoutManager类负责排列。

    • 可以扩展RecyclerView.LayoutManager抽象类来创建自己的布局管理器
    • 可以扩展RecyclerView.ItemAnimator来定义自己的animator对象
    • 可以扩展RecyclerView.ItemDecoration来定义自己的分割线

    同一个RecyclerView可以绑定不同ViewType类型的Item,可以定义接口如下,其他Item实现该接口

    interface IWallpaperListItem {
        val type: Int
    }
    
    class SourceItem : IWallpaperListItem {
        override val type: Int
            get() = ViewType.SOURCE
    }
    
    class RecommendItem(
        val imageList: List<Image>? = null
    ) : IWallpaperListItem {
        override val type: Int
            get() =ViewType.WALLPAPER
    }
    
    1.3 实现Adapter和ViewHolder
    • a. 创建Adapter
      只定义名字
    • b. 创建ViewHolder
      创建ViewHolder的内部类,并且它可以接收一个itemView作为参数。然后创建bind函数,将数据和携带数据的UI关联起来。
    abstract class AbsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        abstract fun bind(wallpaperListItem: IWallpaperListItem)
    }
    
    class ***TitleViewHolder(itemView: View) : AbsViewHolder(itemView) {
        override fun bind(wallpaperListItem: IWallpaperListItem) {
            //绑定内容
        }
    }
    
    class ***SourceViewHolder(itemView: View) : AbsViewHolder(itemView) {
        private val ***CardView = itemView.findViewById<CardView>(R.id.***_card_view)
        private val ***CardView = itemView.findViewById<CardView>(R.id.***_card_view)
        init {
                    //设置背景
            //CardView.background = 
            ***CardView.setOnClickListener {
                Log.d(TAG, "onClick")
                //点击
            }
    
            //***View.background = 
            ***CardView.setOnClickListener {
                Log.d(TAG, "onClick")
                //点击
            }
        }
    
        override fun bind(wallpaperListItem: IWallpaperListItem) {
            //绑定
        }
    
    }
    
    • c. 继承RecyclerView.Adapter
      更新Adapter类的定义,使其继承RecyclerView.Adapter类,并且将ViewHolder作为参数传入
      这里也可以改为继承ListAdapter
    class ***Adapter(
        private val retryLoad: () -> Unit
    ) : ListAdapter<IWallpaperListItem, ***.AbsViewHolder>(***DiffCallback) {
    
    • d. 重写onCreateViewHolder()
      ViewHolder创建的时候会调用该方法。在该方法里进行初始化和填充RecyclerView中的表项视图。
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AbsViewHolder {
            Log.d(TAG, "onBindViewHolder viewType=$viewType")
            return when (viewType) {
                ViewType.SOURCE -> {
                    val view = LayoutInflater.from(parent.context).inflate(R.layout.1_item, parent, false)
                    ***SourceViewHolder(view)
                }
    
                ViewType.TITLE -> {
                    val view = LayoutInflater.from(parent.context).inflate(R.layout.2_title_item, parent, false)
                    ***TitleViewHolder(view)
                }
    
                ViewType.WALLPAPER -> {
                    val view = LayoutInflater.from(parent.context).inflate(R.layout.3_recommend_item, parent, false)
                    ***WallpaperViewHolder(view) { retryLoad() }
                }
    
                else -> {
                    throw IllegalStateException("view type is error")
                }
            }
        }
    
    • e. 重写onBindViewHolder()
      onBindViewHolder()被调用的时候,会传入参数ViewHolder和位置position,该位置可以用于提取表项所需的数据,并且将数据传递给ViewHolder来使数据绑定到对应的UI。
    override fun onBindViewHolder(holder: AbsViewHolder, position: Int) {
            Log.d(TAG, "onBindViewHolder position=$position")
            val ***ListItem = getItem(position)
            holder.bind(***ListItem)
        }
    
    • f. 重写getItemCount()
      RecyclerView显示一个列表,所以它需要知道列表里共有多少项。

    ListAdapter可以处理元素的添加和删除而无需重绘视图,甚至可以为变化添加动画效果。
    DiffUtil 是 ListAdapter 能够高效改变元素的奥秘所在。DiffUtil 会比较新旧列表中增加、移动、删除了哪些元素,然后输出更新操作的列表将原列表中的元素高效地转换为新的元素。

    为了能够识别新的数据,DiffUtil 需要您重写 areItemsTheSame() 和 areContentsTheSame()。areItemsTheSame() 检查两个元素是否为同一元素。areContentsTheSame() 检查两个元素是否包含相同的数据。

    在Adapter类中添加DiffUtil对象,并且复写 areItemsTheSame()和areContentsTheSame()

    object FlowerDiffCallback : DiffUtil.ItemCallback<Flower>() {
        override fun areItemsTheSame(oldItem: Flower, newItem: Flower): Boolean {
            return oldItem == newItem
        }
    
        override fun areContentsTheSame(oldItem: Flower, newItem: Flower): Boolean {
            return oldItem.id == newItem.id
        }
    }
    

    将Adapter的父类由RecyclerView.Adapter改为ListAdapter,并传入DiffCallback

    class FlowersAdapter(private val onClick: (Flower) -> Unit) :
        ListAdapter<Flower, FlowersAdapter.FlowerViewHolder>(FlowerDiffCallback) {
    

    更新列表
    ListAdapter 通过 submitList() 方法获取数据,该方法提交了一个列表来与当前列表进行对比并显示。也就是说您无需再重写 getItemCount(),因为 ListAdapter 会负责管理列表。

    在 Activity 类中,调用 Adapter 的 submitList() 方法并传入数据列表。
    此处需要通过toMutableList转换生成一个新的list,否则不会更新,不同的item,viewType不一样。

    private fun updateListData(imageList: List<ImageData>?) {
            Log.d(TAG, "updateListData imageList.size=${imageList?.size}")
            mItemList.clear()
            mItemList.add(SourceItem())
            mItemList.add(TitleItem())
            mItemList.add(RecommendItem(imageList))
            mAddAdapter.submitList(mItemList.toMutableList())
        }
    
    1.4 连接

    我们已经创建了布局、数据列表和adapter,可以直接将adapter赋给RecyclerView

    2 响应点击事件

    2.1 定义点击动作

    在创建监听器之前,在 Activity 类中添加一个函数用于处理点击之后的响应操作。

    private fun adapterOnClick() {
       val intent = Intent(this,DetailActivity()::class.java)
       this.startActivity(intent)
    }
    

    接下来,修改 Adapter 的构造函数来传入 onClick() 函数。

    class **Adapter(private val onClick: (Flower) -> Unit) :
      ListAdapter<T, RecyclerView.ViewHolder>(DiffCallback())
    

    在 Activity 类中,在初始化 Adapter 的时候传入刚刚创建的点击事件函数。

    2.2 添加onClickHandler()

    现在响应处理已经定义好了,可以将它关联到 Adapter 的 ViewHolder 了。

    修改 ViewHolder,将 onClick() 作为参数传入。

    class ViewHolder(itemView: View, val onClick: (Source) -> Unit) :
      RecyclerView.ViewHolder(itemView)
    

    在初始化的代码中,调用 itemView 的 setOnClickListener{}

    init {
        itemView.setOnClickListener {
           currentItem?.let {
              onClick(it)
           }
        }
    }
    

    现在就可以响应点击事件了

    3 ConcatAdapter

    使用ConcatAdapter可以将多个adapter添加到RecyclerView,ConcatAdapter会依次显示多个Adapter的内容,有兴趣可以了解

    4 参考资料

    https://mp.weixin.qq.com/mp/appmsgalbum?__biz=Mzk0NDIwMTExNw%3D%3D&action=getalbum&album_id=1866978093609893891#wechat_redirect

    相关文章

      网友评论

          本文标题:【UI篇3】RecyclerView创建动态列表

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