安卓关于Recycle布局的学习和使用 适配器

安卓关于Recycle布局的学习和使用 适配器

想要实现下面的效果

image

首先创建 recyclerView

在在 fragment_place.xml ​布局文件中定义了 RecyclerView ​组件

image

第二步:设置 LayoutManager

RecyclerView ​设置了线性布局管理器,使列表项垂直排列显示。

binding.recyclerView.layoutManager = LinearLayoutManager(activity)

第三步:创建适配器

创建了一个 PlaceAdapter ​实例,并将其设置为 RecyclerView ​的适配器。PlaceAdapter{} ​中的空大括号是传递给适配器的点击回调函数,当用户点击列表项时会触发。

adapter = PlaceAdapter {}
binding.recyclerView.adapter = adapter

第一步:定义适配器类

PlaceAdapter 继承自 ListAdapter,使用 DiffUtil 来优化列表更新
接收一个点击回调函数 onItemClick,当用户点击列表项时调用
指定数据类型为 place,ViewHolder 类型为内部类 viewHolder
使用自定义的 PlaceDiffCallback 来进行数据比较

class PlaceAdapter(private val onItemClick: (place) -> Unit): ListAdapter<place,PlaceAdapter.viewHolder>(PlaceDiffCallback())

第二步:创建 ViewHolder

通过 ViewBinding inflate 列表项布局 item_place.xml
创建并返回自定义的 viewHolder 实例

override fun onCreateViewHolder(
    parent: ViewGroup,
    viewType: Int
): viewHolder {
    val binding = ItemPlaceBinding.inflate(LayoutInflater.from(parent.context),parent,false)
    return viewHolder(binding)
}

第三步:绑定数据到 ViewHolder

获取指定位置的数据项
调用 ViewHolder 的 bind 方法绑定数据

override fun onBindViewHolder(
    holder: viewHolder,
    position: Int
) {
    holder.bind(getItem(position))
}

第四步:实现ViewHolder内部类

ViewHolder内部类持有ItemPlaceBinding引用
bind方法负责将place数据绑定到UI组件:
place.name绑定到id为"place"的TextView
place.adm1和place.country组合后绑定到id为"tv_place_addres"的TextView
设置整个item的点击事件,触发传入的onItemClick回调

inner class viewHolder(private val binding: ItemPlaceBinding):ViewHolder(binding.root){
    fun bind(place: place){
        //绑定地名和详细地址名
        binding.place.text = place.name
        binding.tvPlaceAddres.text  = "${place.adm1},${place.country}"
        //设置点击事件
        itemView.setOnClickListener {
            onItemClick(place)
        }
    }
}

第五步:实现DiffUtil回调

PlaceDiffCallback用于比较列表中的新旧数据项
areItemsTheSame通过比较id判断是否为同一项
areContentsTheSame判断内容是否相同

class PlaceDiffCallback: DiffUtil.ItemCallback<place>() {
    override fun areItemsTheSame(oldItem: place, newItem: place): Boolean {
        //判断id是否相同
        return oldItem.id == newItem.id
    }

    //判断内容是否相同
    override fun areContentsTheSame(oldItem: place, newItem: place): Boolean {
        return oldItem == newItem
    }
}

使用适配器

提交数据列表

adapter.submitList(places)

if (places.isNotEmpty()==true){
                        //如果有数据,显示列表,隐藏空状态
                        binding.recyclerView.visibility = View.VISIBLE
                        binding.emptyStateLayout.visibility = View.GONE
                        adapter.submitList(places)
                    }else{
                        //如果没有数据,显示空状态,隐藏列表
                        binding.recyclerView.visibility = View.GONE
                        binding.emptyStateLayout.visibility = View.VISIBLE
                        result.exceptionOrNull()?.printStackTrace()
                    }

拓展 也可以把adapter替换为普通的RecyclerView.Adapter

/**
 * @description: 创建adapter作为绑定$
 * @author: $
 * @date: $ $
 */
class PlaceAdapter(private val onItemClick: (place) -> Unit): RecyclerView.Adapter<PlaceAdapter.viewHolder>(){
    
    private var places = listOf<place>()
    
    fun setPlaces(newPlaces: List<place>) {
        places = newPlaces
        notifyDataSetChanged()
    }

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): viewHolder {
        val binding = ItemPlaceBinding.inflate(LayoutInflater.from(parent.context),parent,false)
        return viewHolder(binding)
    }

    override fun onBindViewHolder(
        holder: viewHolder,
        position: Int
    ) {
        holder.bind(places[position])
    }
    
    override fun getItemCount(): Int = places.size

    inner class viewHolder(private val binding: ItemPlaceBinding): RecyclerView.ViewHolder(binding.root){
        fun bind(place: place){
            //绑定地名和详细地址名
            binding.place.text = place.name
            binding.tvPlaceAddres.text  = "${place.adm1},${place.country}"
            //设置点击事件
            itemView.setOnClickListener {
                onItemClick(place)
            }
        }
    }
}

同时需要修改PlaceFragment中使用adapter的地方:

if (places.isNotEmpty()==true){
                //如果有数据,显示列表,隐藏空状态
                binding.recyclerView.visibility = View.VISIBLE
                binding.emptyStateLayout.visibility = View.GONE
                adapter.setPlaces(places)
            }else{
// ... existing code ...
            //如果输出框为空,则晴空列表并显示空状态
            binding.recyclerView.visibility = View.GONE
            binding.emptyStateLayout.visibility = View.VISIBLE
            adapter.setPlaces(emptyList())
posted @ 2025-08-14 13:46  原来是甘文川同学  阅读(15)  评论(0)    收藏  举报