Android RecyclerView使用总结(Kotlin)

一、前言

最近在学kotlin语言,所以写了这个Demo来练手,顺便复习一下RecyclerView的使用。

Demo效果图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、开始

1、布局文件

这是Demo的主布局文件,由于使用到一个下拉刷新,上拉加载的开源框架,所以先添加一下依赖:

implementation  'com.scwang.smart:refresh-layout-kernel:2.0.3'      //核心必须依赖
implementation  'com.scwang.smart:refresh-header-classics:2.0.3'    //经典刷新头
implementation  'com.scwang.smart:refresh-footer-classics:2.0.3'    //经典加载

SmartRefreshLayout框架地址:https://github.com/scwang90/SmartRefreshLayout

<?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"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/addBtn"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginLeft="4dp"
            android:layout_weight="1"
            android:text="增加"
            android:textAllCaps="false"
            android:textSize="14sp" />

        <Button
            android:id="@+id/updateBtn"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginLeft="4dp"
            android:layout_weight="1"
            android:text="更新"
            android:textAllCaps="false" />

        <Button
            android:id="@+id/removeBtn"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginLeft="4dp"
            android:layout_weight="1"
            android:text="删除"
            android:textAllCaps="false" />

        <Button
            android:id="@+id/clearBtn"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginLeft="4dp"
            android:layout_weight="1"
            android:text="清空"
            android:textAllCaps="false" />
    </LinearLayout>

    <com.scwang.smart.refresh.layout.SmartRefreshLayout
        android:id="@+id/refreshLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.scwang.smart.refresh.header.ClassicsHeader
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:overScrollMode="never" />

        <com.scwang.smart.refresh.footer.ClassicsFooter
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </com.scwang.smart.refresh.layout.SmartRefreshLayout>

</LinearLayout>

recyclerView为线性布局的item条目的布局:
(另外两种布局的item布局就不放了,跟这个差不多)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="wrap_content"
    android:background="#FFFFFF"
    android:orientation="horizontal"
    android:padding="5dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/fruit_image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:srcCompat="@drawable/apple_pic" />

        <TextView
            android:id="@+id/fruit_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginLeft="10dp"
            android:text="TextView"
            android:textColor="@color/black"
            android:textSize="16sp" />
    </LinearLayout>

</RelativeLayout>

2、初始化用到的数据

准备一下recyclerView用到的实体类数据。
先创建实体类Fruit:

class Fruit(var name:String, var imageId:Int) {
}

初始化数据:

private val fruitList = ArrayList<Fruit>()

private fun initData() {
        repeat(2) {
            fruitList.add(Fruit("apple", R.drawable.apple_pic))
            fruitList.add(Fruit("banana", R.drawable.banana_pic))
            fruitList.add(Fruit("orange", R.drawable.orange_pic))
            fruitList.add(Fruit("watermelon", R.drawable.watermelon_pic))
            fruitList.add(Fruit("pear", R.drawable.pear_pic))
            fruitList.add(Fruit("strawberry", R.drawable.strawberry_pic))
            fruitList.add(Fruit("mango", R.drawable.mango_pic))
            fruitList.add(Fruit("grape", R.drawable.grape_pic))
            fruitList.add(Fruit("pineapple", R.drawable.pineapple_pic))
        }
        showList(true)
    }

3、recyclerView设置布局管理器

给recyclerView设置一个布局管理器,有三种管理器,也可以自定义布局管理器。

类名含义
LinearLayoutManager线性布局管理器
GridLayoutManager网格布局管理器
StaggeredGridLayoutManager交错网格布局管理器(瀑布流布局)
val linearLayout = LinearLayoutManager(this)
linearLayout.orientation = LinearLayoutManager.VERTICAL
recyclerView.layoutManager = linearLayout

可以通过更改布局管理器实例的orientation属性的值来改变布局方向,水平或者垂直。

4、适配器

编写适配器类是使用RecyclerView最重要的内容。
由于我这个Demo有多种不同布局的RecyclerView,所以我对适配类进行了抽取,创建一个RecyclerViewBaseAdapter类继承RecyclerView.Adapter,复写三个重要的方法onCreateViewHolder()getItemCount(),onBindViewHolder()
然后创建一个内部类MyViewHolder继承自RecyclerView.ViewHolder。
创建一个抽象方法getSubView() 该方法的作用就是让子类来提供要加载的item布局。

package com.example.recyclerviewkt

import android.telecom.Connection
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import java.util.*
import kotlin.collections.ArrayList

abstract class RecyclerViewBaseAdapter(val fruitList:ArrayList<Fruit>) : RecyclerView.Adapter<RecyclerViewBaseAdapter.MyViewHolder>(),
    MyItemTouchCallback.ItemTouchResultCallback {

    abstract fun getSubView(parent: ViewGroup, viewType: Int):View

    inner class MyViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){

        val fruitName : TextView = itemView.findViewById(R.id.fruit_name)
        val fruitImage : ImageView = itemView.findViewById(R.id.fruit_image)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val itemView = getSubView(parent,viewType)
        val viewHolder = MyViewHolder(itemView)
        return viewHolder
    }

    override fun getItemCount() = fruitList.size

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val fruit:Fruit = fruitList[position]
        holder.fruitName.text = fruit.name
        holder.fruitImage.setImageResource(fruit.imageId)
    }

创建FruitListAdapter类继承RecyclerViewBaseAdapter,
复写getSubView()方法,返回item条目的布局文件

package com.example.recyclerviewkt

import android.view.View
import android.view.ViewGroup

class FruitListAdapter(fruitList: ArrayList<Fruit>) : RecyclerViewBaseAdapter(fruitList) {

    override fun getSubView(parent: ViewGroup, viewType: Int) = View.inflate(parent.context, R.layout.item_list_view, null)
}
}

给recyclerView配置适配器:

mAdapter = FruitListAdapter(fruitList)
recyclerView.adapter = mAdapter

5、设置点击事件

给recyclerView的item设置点击事件,或者item布局中的控件设置点击事件。
先在RecyclerViewBaseAdapter中创建点击事件的回调接口,并提供注册点击事件监听的方法:

    interface OnItemClickListener{
        fun onItemClick(position:Int)
    }
    fun setOnItemClickListener(listener:OnItemClickListener){
        this.mListener = listener
    }

在onCreateViewHolder()方法中设置点击事件,完整代码:

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val itemView = getSubView(parent,viewType)
        val viewHolder = MyViewHolder(itemView)
        viewHolder.itemView.setOnClickListener {
            //整个item的点击事件处理
            val position = viewHolder.adapterPosition
            //在这里直接处理或通知到外面处理
            //Toast.makeText(parent.context,"你点击了" + fruitList[position].name + "条目",Toast.LENGTH_SHORT).show()
            mListener.onItemClick(position)
        }
        viewHolder.fruitImage.setOnClickListener {
            //图片的点击事件处理
            val position = viewHolder.adapterPosition
            //Log.d(TAG,"position : " + position)
            Toast.makeText(parent.context,"你点击了" + fruitList[position].name + "图片",Toast.LENGTH_SHORT).show()
        }
        return viewHolder
    }

然后在完成适配器对象创建后,注册点击事件监听,让当前类实现RecyclerViewBaseAdapter.OnItemClickListener接口:

mAdapter.setOnItemClickListener(this)

在回调方法中对点击事件进行处理:

override fun onItemClick(position: Int) {
        //item点击事件处理
        Toast.makeText(this, "你点击了" + fruitList[position].name + "条目", Toast.LENGTH_SHORT).show()
    }

6、下拉刷新,上拉加载更多

使用开源框架SmartRefreshLayout完成该功能。
使用方法:
首先在布局文件中添加需要的控件,具体看上面布局代码。然后在代码中设置对上拉、下拉的监听,并进行相应的处理(添加数据或更新数据):

//下拉刷新
refreshLayout.setOnRefreshListener {
      fruitList[0].name = "cherry"
      fruitList[0].imageId = R.drawable.cherry_pic
      mAdapter.notifyDataSetChanged()
      it.finishRefresh(1000)
	}
//上拉加载更多
      refreshLayout.setOnLoadMoreListener { 			
      		mAdapter.add(fruitList.size,Fruit("cherry",R.drawable.cherry_pic))
            recyclerView.scrollToPosition(fruitList.size-1)
            it.finishLoadMore(1000)
        }

7、拖拽移动、滑动删除

实现该功能需要用到ItemTouchHelper这个类,创建一个该类实例时需要传入一个ItemTouchHelper.Callback对象,创建该对象需要我们自己定义一个类继承ItemTouchHelper.Callback,从而对item的滑动和拖拽进行配置:
对于该类的解释可以看代码中的注释

package com.example.recyclerviewkt

import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView

class MyItemTouchCallback(val mCallback:ItemTouchResultCallback) : ItemTouchHelper.Callback(){

    /**
     *  ItemTouchHelper.UP    向上
     *  ItemTouchHelper.DOWN  向下
     *  ItemTouchHelper.LEFT  向左
     *  ItemTouchHelper.RIGHT 向右
     *  ItemTouchHelper.START 从右向左
     *  ItemTouchHelper.END   从左向右
     *  如果某个值传0,表示不触发该操作,
     *  此处设置支持上下拖拽和向右滑动
     */
    override fun getMovementFlags(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder
    ) = makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN,ItemTouchHelper.END)


    /**
     * 拖拽操作的回调
     * @param viewHolder 被拖动的ViewHolder
     * @param target     目标位置的ViewHolder
     * @return 如果item发生位置交换,返回true,否则返回false
     */
    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        mCallback.onItemMove(viewHolder.adapterPosition,target.adapterPosition)
        return true
    }

    /**
     * 滑动操作的回调
     * @param viewHolder 滑动的ViewHolder
     * @param direction  滑动的方向
     */
    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        mCallback.onItemDelete(viewHolder.adapterPosition)
    }

    interface ItemTouchResultCallback{
        //拖拽移动
        fun onItemMove(fromPosition:Int,toPosition:Int)
        //滑动删除
        fun onItemDelete(position:Int)
    }
}

同时我们还要在类中声明一个回调接口ItemTouchResultCallback,用来通知外部对滑动和拖拽操作进行相应的数据处理。
创建完上面这个类后,让recyclerView和该类实例进行绑定,从而使recyclerView可以滑动或拖拽item。

val itemTouchHelperCallback = MyItemTouchCallback(mAdapter)
val itemTouchHelper = ItemTouchHelper(itemTouchHelperCallback)
itemTouchHelper.attachToRecyclerView(recyclerView)

最后,让适配器类实现上面说到的MyItemTouchCallback.ItemTouchResultCallback回调接口,在回调方法中进行相应的数据处理。

/**
     * 拖拽移动item
     */
    override fun onItemMove(fromPosition: Int, toPosition: Int) {
        Collections.swap(fruitList,fromPosition,toPosition)
        notifyItemMoved(fromPosition,toPosition)
    }

    /**
     * 向右滑动删除item
     */
    override fun onItemDelete(position: Int) {
        fruitList.removeAt(position)
        notifyItemRemoved(position)
    }

8、适配器完整代码

package com.example.recyclerviewkt

import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import java.util.*
import kotlin.collections.ArrayList

abstract class RecyclerViewBaseAdapter(val fruitList:ArrayList<Fruit>) : RecyclerView.Adapter<RecyclerViewBaseAdapter.MyViewHolder>(),
    MyItemTouchCallback.ItemTouchResultCallback {

    private val TAG = "RecyclerViewBaseAdapter"
    private lateinit var mListener: OnItemClickListener

    abstract fun getSubView(parent: ViewGroup, viewType: Int):View

    inner class MyViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){

        val fruitName : TextView = itemView.findViewById(R.id.fruit_name)
        val fruitImage : ImageView = itemView.findViewById(R.id.fruit_image)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val itemView = getSubView(parent,viewType)
        val viewHolder = MyViewHolder(itemView)
        viewHolder.itemView.setOnClickListener {
            //整个item的点击事件处理
            val position = viewHolder.adapterPosition
            //在这里直接处理或通知到外面处理
            //Toast.makeText(parent.context,"你点击了" + fruitList[position].name + "条目",Toast.LENGTH_SHORT).show()
            mListener.onItemClick(position)
        }
        viewHolder.fruitImage.setOnClickListener {
            //图片的点击事件处理
            val position = viewHolder.adapterPosition
            //Log.d(TAG,"position : " + position)
            Toast.makeText(parent.context,"你点击了" + fruitList[position].name + "图片",Toast.LENGTH_SHORT).show()
        }
        return viewHolder
    }

    override fun getItemCount() = fruitList.size

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val fruit:Fruit = fruitList[position]
        holder.fruitName.text = fruit.name
        holder.fruitImage.setImageResource(fruit.imageId)
    }

    /**
     * 增加条目
     */
    fun add(position: Int,fruit: Fruit){
        fruitList.add(position,fruit)
        notifyItemInserted(position)
        //notifyDataSetChanged()
    }

    /**
     * 更新条目
     */
    fun update(position: Int){
        fruitList[position].name = "这是经过修改的数据"
        fruitList[position].imageId = R.drawable.grape_pic
        notifyItemChanged(position)
    }

    /**
     * 删除条目
     */
    fun remove(position: Int){
        fruitList.removeAt(position)
        notifyItemRemoved(position)
    }

    /**
     * 清空条目
     */
    fun clear(){
        fruitList.clear()
        notifyDataSetChanged()
    }

    /**
     * 注册item点击事件
     */
    fun setOnItemClickListener(listener:OnItemClickListener){
        this.mListener = listener
    }

    /**
     * item点击事件的回调接口
     */
    interface OnItemClickListener{
        fun onItemClick(position:Int)
    }

    /**
     * 拖拽移动item
     */
    override fun onItemMove(fromPosition: Int, toPosition: Int) {
        Collections.swap(fruitList,fromPosition,toPosition)
        notifyItemMoved(fromPosition,toPosition)
    }

    /**
     * 向右滑动删除item
     */
    override fun onItemDelete(position: Int) {
        fruitList.removeAt(position)
        notifyItemRemoved(position)
    }
}

9、含多种item布局的recyclerView

要让recyclerView加载不同的item布局,关键就是根据不同的item类型加载不同的ViewHolder,所以我们需要创建多种ViewHolder,我这里创建了三种ViewHolder:

inner class BigImageViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){
        val imageView:ImageView = itemView.findViewById(R.id.fruit_image)
    }

    inner class ThreeImageViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){
        val imageView:ImageView = itemView.findViewById(R.id.fruit_image)
    }

    inner class RightImageViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){
        val imageView:ImageView = itemView.findViewById(R.id.fruit_image)
    }

item数据实体类要含有标明item种类的type变量,这是我声明的一个实体类:

package com.example.recyclerviewkt

class ItemWithType(val imageId:Int, val type:Int) {
}

如何在创建ViewHolder时知道当前item是那种类型,关键就在于getItemViewType()方法,该方法返回当前位置的item数据的类型:

override fun getItemViewType(position: Int): Int {
        return itemList[position].type
    }

从而在执行onCreateViewHolder(parent: ViewGroup, viewType: Int)方法创建ViewHolder时,就可以知道当前item是哪种类型了。
完整适配器代码如下:

package com.example.recyclerviewkt

import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView

class MoreTypeItemAdapter(val itemList:ArrayList<ItemWithType>): RecyclerView.Adapter<RecyclerView.ViewHolder>(){

    companion object{
        @JvmStatic
        val TYPE_BIG_IMAGE = 0
        @JvmStatic
        val TYPE_THREE_IMAGE = 1
        @JvmStatic
        val TYPE_RIGHT_IMAGE = 2
    }

    inner class BigImageViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){
        val imageView:ImageView = itemView.findViewById(R.id.fruit_image)
    }

    inner class ThreeImageViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){
        val imageView:ImageView = itemView.findViewById(R.id.fruit_image)
    }

    inner class RightImageViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){
        val imageView:ImageView = itemView.findViewById(R.id.fruit_image)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return when(viewType){
            TYPE_BIG_IMAGE -> {
                BigImageViewHolder(View.inflate(parent.context,R.layout.item_big_image_view,null))
            }
            TYPE_THREE_IMAGE -> {
                val itemView = View.inflate(parent.context,R.layout.item_three_image_view,null)
                ThreeImageViewHolder(itemView)
            }
            TYPE_RIGHT_IMAGE -> {
                val itemView = View.inflate(parent.context,R.layout.item_right_image_view,null)
                RightImageViewHolder(itemView)
            }
            else -> {
                val itemView = View.inflate(parent.context,R.layout.item_right_image_view,null)
                RightImageViewHolder(itemView)
            }
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val imageId = itemList[position].imageId
        when(getItemViewType(position)){
            TYPE_BIG_IMAGE -> {
                (holder as BigImageViewHolder).imageView.setImageResource(imageId)
            }
            TYPE_THREE_IMAGE -> {
                (holder as ThreeImageViewHolder).imageView.setImageResource(imageId)
            }
            TYPE_RIGHT_IMAGE -> {
                (holder as RightImageViewHolder).imageView.setImageResource(imageId)
            }
        }
    }

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

    override fun getItemViewType(position: Int): Int {
        return itemList[position].type
    }
}

三、结尾

该Demo的完整源码地址:
https://github.com/gitHub-lgh/RecyclerViewKtDemo/tree/master

如果这篇文章给你带来了一点帮助,请点个赞吧,谢谢。

  • 15
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于RecyclerView的分页,您可以使用SmartRefreshLayout和Kotlin来实现。以下是一个示例代码: 首先,在您的布局文件中,将RecyclerView包含在SmartRefreshLayout中: ```xml <com.scwang.smartrefresh.layout.SmartRefreshLayout android:id="@+id/refreshLayout" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" /> </com.scwang.smartrefresh.layout.SmartRefreshLayout> ``` 接下来,在您的Activity或Fragment中,初始化SmartRefreshLayout和RecyclerView,并设置分页加载的逻辑: ```kotlin val refreshLayout = findViewById<SmartRefreshLayout>(R.id.refreshLayout) val recyclerView = findViewById<RecyclerView>(R.id.recyclerView) // 设置LayoutManager和Adapter val layoutManager = LinearLayoutManager(this) recyclerView.layoutManager = layoutManager val adapter = YourRecyclerViewAdapter() recyclerView.adapter = adapter // 设置分页加载的逻辑 var page = 1 val pageSize = 10 refreshLayout.setOnRefreshListener { // 下拉刷新逻辑 page = 1 loadData(page, pageSize) } refreshLayout.setOnLoadMoreListener { // 上拉加载逻辑 page++ loadData(page, pageSize) } // 加载数据的方法 private fun loadData(page: Int, pageSize: Int) { // 根据page和pageSize加载数据 // 这里可以使用异步请求数据的方式,比如使用Retrofit或者使用协程等 // 加载完成后,更新RecyclerView的数据 // adapter.updateData(data) // 根据加载结果,判断是否还有更多数据,如果没有可以调用refreshLayout.finishLoadMoreWithNoMoreData()方法 } ``` 在loadData方法中,您可以使用异步请求数据的方式来加载数据,并在加载完成后更新RecyclerView的数据。根据加载结果,您还可以判断是否还有更多数据可供加载,如果没有,则调用refreshLayout.finishLoadMoreWithNoMoreData()方法来禁用加载更多功能。 这样,您就可以使用SmartRefreshLayout和Kotlin来实现RecyclerView的分页加载了。希望能对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值