Android开发基础——RecyclerView

RecyclerView是比LitView更为强大的控件,其优化了ListView的不足。Android官方也更推荐使用RecyclerView。

基本用法

RecyclerView属于新增控件,Google将RecyclerView控件定义在AndroidX当中,用户只需要在项目的build.gradle中添加RecyclerView库的依赖,就能够保证在所有Android系统版本上都可以使用RecyclerView控件。

在app/build.gradle文件中的dependencis中添加如下内容:

dependencies {

    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'androidx.recyclerview:recyclerview:1.2.0'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

然后修改布局中的代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    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"/>

</LinearLayout>

由于RecyclerView并不是内置在系统SDK中的,因此需要完整的包路径。

这里构造和之前同样的Fruit类和fruit_item.xml布局文件。

然后构建新的FruitAdapter适配器,使该适配器继承自RecyclerView.Adapter,并将泛型指定为FruitAdapter.ViewHolder。

class FruitAdapter(val fruitList:List<Fruit>):
    RecyclerView.Adapter<FruitAdapter.ViewHolder>(){

    inner class ViewHolder(view:View):RecyclerView.ViewHolder(view) {
        val fruitImage: ImageView = view.findViewById(R.id.fruitImage)
        val fruitName: TextView = view.findViewById(R.id.fruitName)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item, parent, false)
        return ViewHolder(view)
    }

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

    override fun getItemCount() = fruitList.size
}

上面是RecyclerView适配器的标准写法,首先定义一个内部类ViewHolder,其继承自RecyclerView.ViewHolder,然后ViewHolder的主构造函数要传入一个View参数,该参数通常就是RecyclerView子项的最外层布局,然后用户就可以通过findViewById获取布局中ImageView和TextView的实例了。

而FruitAdapter继承自RecyclerView.Adapter,就必须要重写onCreateViewHolder/onBindViewHolder/getItemCount三个方法:

  • onCreateViewHolder:用于创建ViewHolder实例,在该方法中加载fruit_item布局,然后创建一个ViewHolder实例,并将加载出来的布局传入构造函数中,最后将ViewHolder的实例返回
  • onBindViewHolder:用于对RecyclerView子项的数据进行复制,会在每个子项被滚动到屏幕内的时候执行,这里通过position参数得到当前项的Fruit实例,然后将之设置到ViewHolder
  • getItemCount:返回数据源的长度
class MainActivity : AppCompatActivity() {

    private val fruitList = ArrayList<Fruit>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initFruits()
        val layoutManager = LinearLayoutManager(this)
        recyclerView.layoutManager = layoutManager
        val adapter = FruitAdapter(fruitList)
        recyclerView.adapter = adapter
    }

    private fun initFruits() {
        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("Grape", R.drawable.grape_pic))
            fruitList.add(Fruit("Pineapple", R.drawable.pineapple_pic))
            fruitList.add(Fruit("Strawberry", R.drawable.strawberry_pic))
            fruitList.add(Fruit("Cherry", R.drawable.cherry_pic))
            fruitList.add(Fruit("Mango", R.drawable.mango_pic))
        }
    }
}

可以看到,这里设置了同样的initFruits方法,用于初始化所有的数据,然后在onCreate方法中先创建LinearLayoutManager对象,并将之设置到RecyclerView中。LayoutManager用于指定RecyclerView的布局方式,这里使用的是LinearLayoutManager是线性布局的意思,可以实现和ListView类似的效果。之后创建了FruitAdapter实例,并将数据传入FruitAdapter的构造函数中,最后调用RecyclerView的setAdapter方法来完成适配器设置。程序运行后的结果为:

 实现横向滚动和瀑布流布局

ListView扩展性并不好,其只能实现纵向滚动的效果,而RecyclerView却还能够实现横向流动的效果。

首先对fruit_item布局进行修改:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="80dp"
    android:layout_height="match_content">

    <ImageView
        android:id="@+id/fruitImage"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="center_vertical"
        android:layout_marginTop="10dp"/>

    <TextView
        android:id="@+id/fruitName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginTop="10dp"/>

</LinearLayout>

之前布局元素是水平排列的,这里将之修改为垂直排列。然后修改Activity中的代码:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initFruits()
        val layoutManager = LinearLayoutManager(this)
        layoutManager.orientation = LinearLayoutManager.HORIZONTAL
        recyclerView.layoutManager = layoutManager
        val adapter = FruitAdapter(fruitList)
        recyclerView.adapter = adapter
    }

这里只是将layoutManager的排列方向设为了水平方向。程序运行后的结果为:

 RecyclerView之所以能够实现ListView无法实现的效果是因为RecyclerView的布局管理由LayoutManager完成。除了LinearLayoutManager之外,RecyclerView还提供了GridLayoutManager和StaggeredGridLayoutManager这两种内置的布局排列方式。前者可以实现网格布局,后者则可以实现瀑布流布局。

这里看一下瀑布流布局,首先是修改fruit_item布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:margin="5dp">

    <ImageView
        android:id="@+id/fruitImage"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="center_vertical"
        android:layout_marginTop="10dp"/>

    <TextView
        android:id="@+id/fruitName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:layout_marginTop="10dp"/>

</LinearLayout>

这里将LinearLayout的宽度改为了match_parent,这是因为瀑布流布局的宽度应该是根据布局的列数自动适配的,而不是固定值。然后修改Activity中的代码:

class MainActivity : AppCompatActivity() {

    private val fruitList = ArrayList<Fruit>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initFruits()
        val layoutManager = StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL)
        recyclerView.layoutManager = layoutManager
        val adapter = FruitAdapter(fruitList)
        recyclerView.adapter = adapter
    }

    private fun initFruits() {
        repeat(2) {
            fruitList.add(Fruit(getRandomLengthString("Apple"), R.drawable.apple_pic))
            fruitList.add(Fruit(getRandomLengthString("Banana"), R.drawable.banana_pic))
            fruitList.add(Fruit(getRandomLengthString("Orange"), R.drawable.orange_pic))
            fruitList.add(Fruit(getRandomLengthString("Watermelon"), R.drawable.watermelon_pic))
            fruitList.add(Fruit(getRandomLengthString("Pear"), R.drawable.pear_pic))
            fruitList.add(Fruit(getRandomLengthString("Grape"), R.drawable.grape_pic))
            fruitList.add(Fruit(getRandomLengthString("Pineapple"), R.drawable.pineapple_pic))
            fruitList.add(Fruit(getRandomLengthString("Strawberry"), R.drawable.strawberry_pic))
            fruitList.add(Fruit(getRandomLengthString("Cherry"), R.drawable.cherry_pic))
            fruitList.add(Fruit(getRandomLengthString("Mango"), R.drawable.mango_pic))
        }
    }

    private fun getRandomLengthString(str:String):String {
        val n = (1..20).random()
        val builder = StringBuilder()
        repeat(n) {
            builder.append(str)
        }
        return builder.toString()
    }
}

其实上面的代码并没有修改多少,只是将原有的LinearLayoutManager修改为StaggeredGridLayoutManager。StaggeredGridLayoutManager的构造函数接收两个参数,第一个参数用于指定布局的列数,3表示3列,第二个参数用于指定布局的排列方向,这里为纵向排列。同时这里使用getRandomLengthString生成不同长度的字符串来显示瀑布流的效果。程序运行结果为:

 RecyclerView的点击事件

和ListView不同,RecyclerView需要用户自己给子项具体的View注册点击事件。

修改FruitAdapter中的代码:

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item, parent, false)
        val viewHolder = ViewHolder(view)

        viewHolder.itemView.setOnClickListener {
            val position = viewHolder.bindingAdapterPosition
            val fruit = fruitList[position]
            Toast.makeText(parent.context, "You clicked view ${fruit.name}", Toast.LENGTH_SHORT).show()
        }

        viewHolder.fruitImage.setOnClickListener {
            val position = viewHolder.bindingAdapterPosition
            val fruit = fruitList[position]
            Toast.makeText(parent.context, "You clicked image ${fruit.name}", Toast.LENGTH_SHORT).show()
        }

        return viewHolder
    }

在上面的代码中,仍然是在onCreateViewHolder中注册点击事件,并同时为最外层布局和ImageView都注册了点击事件,itemView表示最外层布局,fruitImage表示图片,这就是RecyclerView的特别之处。程序运行结果为:

  而显然点击文本是不会有响应的。

<think>嗯,用户这次问的是Android开发RecyclerView切换Adapter的具体实现方法。从问题来看,用户应该是有实际开发需求,可能正在做一个需要动态切换列表内容的界面。用户提供的几个引用信息挺有意思。引用[1]和[2]都是关于RecyclerView基础使用的,包括布局和依赖配置。引用[3]提到的Gloading库倒是个意外,看起来用户之前可能还关注过加载状态的处理,不过这次问题没直接关联。用户能提供这些引用,说明ta确实做了些功课。我注意到用户上次问过快排的实现,这次转向Android开发,跨度不小啊。可能是位全栈开发者,或者正在学习不同领域的技术。用户特意提到"不是真正需求",说明ta清楚知道自己要什么,这点很好。针对RecyclerView切换Adapter,常见场景有:tab切换不同数据视图、搜索过滤、状态切换显示空布局等。核心实现其实不复杂,但要注意几个关键点:1)Adapter解耦2)切换时数据同步3)性能优化4)空状态处理。用户要的是示例代码,那得给个典型实现。可以展示两个不同数据结构的Adapter切换,比如从联系人列表切到消息列表。记得提醒setLayoutManager重用的问题,很多人会忘记这点导致性能损耗。另外平滑滚动到顶部的细节也很实用。Gloading那个引用虽然不直接相关,但可以融入到建议里作为加分项——在切换时添加加载动画提升体验。用户上次问过快排,说不定对性能优化敏感,可以提一下DiffUtil的使用建议。最后生成的问题要覆盖进阶使用,比如动态类型切换和ViewHolder重用,这些才是实际开发中的痛点。用户能想到问切换Adapter,应该不是初学者了,问题可以设计得深入些。</think> 以下是在 Android 中实现 `RecyclerView` 切换 `Adapter` 的核心步骤和示例代码,参考了站内引用中关于 `RecyclerView` 和布局加载的实践[^1][^2],并结合了 `Gloading` 对加载状态的处理思路[^3]: --- ### 一、实现原理 1. **Adapter解耦**:每个数据类型独立一个 Adapter(如 `UserAdapter`、`ProductAdapter`),保证单一职责。 2. **切换逻辑**:通过 `RecyclerView.setAdapter()` 动态更换 Adapter,需重新绑定数据。 3. **状态管理**:参考 `Gloading` 的思想,切换时可添加加载动画避免界面闪烁[^3]。 --- ### 二、示例代码 ```kotlin // 1. 定义两个不同的Adapter class UserAdapter(val userList: List<User>) : RecyclerView.Adapter<UserViewHolder>() { /*...*/ } class ProductAdapter(val productList: List<Product>) : RecyclerView.Adapter<ProductViewHolder>() { /*...*/ } // 2. 在Activity/Fragment中切换 class MainActivity : AppCompatActivity() { private lateinit var recyclerView: RecyclerView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 引用[2]的布局 recyclerView = findViewById(R.id.recyclerview) recyclerView.layoutManager = LinearLayoutManager(this) // 默认显示用户列表 switchToUserAdapter() } // 切换到用户Adapter fun switchToUserAdapter() { showLoading() // 参考[^3]的加载状态 val users = fetchUsers() // 模拟数据加载 recyclerView.adapter = UserAdapter(users).apply { // 可选:添加动画避免闪烁 notifyDataSetChanged() } hideLoading() } // 切换到商品Adapter fun switchToProductAdapter() { showLoading() val products = fetchProducts() recyclerView.adapter = ProductAdapter(products) hideLoading() } private fun showLoading() { // 使用Gloading或ProgressBar显示加载状态[^3] } } ``` --- ### 三、关键优化 1. **复用RecyclerView**: 避免重复创建 `RecyclerView`,始终复用同一个实例(如引用[1]的布局)。 2. **数据更新效率**: 使用 `DiffUtil` 增量更新数据,而非 `notifyDataSetChanged()`。 3. **状态一致性**: 切换时若数据加载耗时,参考 `Gloading` 显示加载框[^3]提升体验。 --- ### 四、布局文件(activity_main.xml) ```xml <!-- 引用[1][^1]的RecyclerView基础布局 --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerview" android:layout_width="match_parent" android:layout_height="match_parent" /> <!-- 可在此处添加加载状态布局(参考Gloading[^3]) --> <FrameLayout android:id="@+id/loading_container" android:visibility="gone" ... /> ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值