You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android技术问题:如何在RecyclerView中操作JSON数据并实现项点击筛选

Hey,我来一步步帮你解决这两个在Android订餐类APP里超常见的RecyclerView问题!

1. 如何在RecyclerView中获取并设置JSON数据?

整个流程可以拆成准备实体类、获取并解析JSON、绑定RecyclerView适配器这三步,我给你具体的代码示例:

第一步:创建对应JSON结构的实体类

首先得把后端返回的JSON转换成Java/Kotlin的实体类,这样才能方便操作数据。假设你的JSON数据是这样的:

[
  {
    "name": "宫保鸡丁",
    "rate": 4.8,
    "amount": 28,
    "desc": "经典川菜,酸甜适口"
  },
  {
    "name": "鱼香肉丝",
    "rate": 4.7,
    "amount": 26,
    "desc": "咸香微辣,下饭神器"
  }
]

那对应的Kotlin实体类可以这么写(自带getter/setter,还支持序列化):

@Parcelize
data class FoodItem(
    val name: String,
    val rate: Double,
    val amount: Int,
    val desc: String
) : Parcelable

如果是Java的话,就写普通的Bean类,记得加构造方法、getter和setter,或者用Lombok简化代码。

第二步:获取并解析JSON数据

我用常用的OkHttp来举例子,你也可以用Retrofit(更适合复杂接口场景):

val okHttpClient = OkHttpClient()
val request = Request.Builder()
    .url("你的JSON接口地址") // 比如http://xxx.com/api/foods
    .build()

okHttpClient.newCall(request).enqueue(object : Callback {
    override fun onFailure(call: Call, e: IOException) {
        // 请求失败的处理,比如弹个Toast提示用户
        runOnUiThread {
            Toast.makeText(this@MainActivity, "加载失败", Toast.LENGTH_SHORT).show()
        }
        e.printStackTrace()
    }

    override fun onResponse(call: Call, response: Response) {
        response.body?.string()?.let { jsonStr ->
            // 用Gson解析JSON成实体类列表
            val foodList = Gson().fromJson(jsonStr, Array<FoodItem>::class.java).toList()
            // 必须回到主线程更新UI
            runOnUiThread {
                setupRecyclerView(foodList)
            }
        }
    }
})

别忘了在build.gradle里添加OkHttp和Gson的依赖哦!

第三步:绑定RecyclerView适配器

先写一个简单的适配器,把数据渲染到列表项里:

class FoodAdapter(private val foodList: List<FoodItem>) : RecyclerView.Adapter<FoodAdapter.FoodViewHolder>() {

    inner class FoodViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val tvFoodName: TextView = itemView.findViewById(R.id.tv_food_name)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FoodViewHolder {
        // 加载列表项布局
        val itemView = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_food, parent, false)
        return FoodViewHolder(itemView)
    }

    override fun onBindViewHolder(holder: FoodViewHolder, position: Int) {
        val food = foodList[position]
        // 把名称设置到TextView里
        holder.tvFoodName.text = food.name
    }

    override fun getItemCount() = foodList.size
}

然后写setupRecyclerView方法初始化RecyclerView:

private fun setupRecyclerView(foodList: List<FoodItem>) {
    val rvFood = findViewById<RecyclerView>(R.id.rv_food)
    rvFood.layoutManager = LinearLayoutManager(this) // 线性布局,你也可以用GridLayoutManager
    rvFood.adapter = FoodAdapter(foodList)
}
2. 点击列表项名称时,展示对应的Rate、Amount等关联数据

这里有三种常见的实现方式,你可以根据APP的交互需求选择:

方式一:在当前列表项展开/收起详情

适合不想跳转页面的轻量展示,修改一下适配器和布局就行:

  1. 先修改列表项布局item_food.xml,把详情部分默认隐藏:
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:id="@+id/tv_food_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:textStyle="bold"/>

    <!-- 详情区域,默认隐藏 -->
    <LinearLayout
        android:id="@+id/ll_food_details"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:visibility="gone"
        android:layout_marginTop="8dp">

        <TextView
            android:id="@+id/tv_food_rate"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="评分:4.8"/>

        <TextView
            android:id="@+id/tv_food_amount"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="价格:28元"/>
    </LinearLayout>
</LinearLayout>
  1. 修改适配器,添加点击事件和展开状态记录:
class FoodAdapter(private val foodList: List<FoodItem>) : RecyclerView.Adapter<FoodAdapter.FoodViewHolder>() {
    // 记录哪些项是展开状态
    private val expandedPositions = mutableSetOf<Int>()

    inner class FoodViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val tvFoodName: TextView = itemView.findViewById(R.id.tv_food_name)
        val llFoodDetails: LinearLayout = itemView.findViewById(R.id.ll_food_details)
        val tvFoodRate: TextView = itemView.findViewById(R.id.tv_food_rate)
        val tvFoodAmount: TextView = itemView.findViewById(R.id.tv_food_amount)

        init {
            // 给名称TextView设置点击事件
            tvFoodName.setOnClickListener {
                val position = adapterPosition
                if (position != RecyclerView.NO_POSITION) {
                    if (expandedPositions.contains(position)) {
                        // 收起详情
                        expandedPositions.remove(position)
                        llFoodDetails.visibility = View.GONE
                    } else {
                        // 展开详情并设置数据
                        expandedPositions.add(position)
                        llFoodDetails.visibility = View.VISIBLE
                        val food = foodList[position]
                        tvFoodRate.text = "评分:${food.rate}分"
                        tvFoodAmount.text = "价格:${food.amount}元"
                    }
                    // 更新当前项的UI
                    notifyItemChanged(position)
                }
            }
        }
    }

    override fun onBindViewHolder(holder: FoodViewHolder, position: Int) {
        val food = foodList[position]
        holder.tvFoodName.text = food.name
        // 根据展开状态设置详情区域的可见性
        holder.llFoodDetails.visibility = if (expandedPositions.contains(position)) View.VISIBLE else View.GONE
        // 如果是展开状态,提前设置好数据
        if (expandedPositions.contains(position)) {
            holder.tvFoodRate.text = "评分:${food.rate}分"
            holder.tvFoodAmount.text = "价格:${food.amount}元"
        }
    }

    // 其他方法不变...
}

方式二:跳转到详情页面展示

适合需要展示更多详细信息的场景,比如菜品描述、图片等:

  1. 给适配器添加点击回调:
class FoodAdapter(
    private val foodList: List<FoodItem>,
    private val onItemClick: (FoodItem) -> Unit
) : RecyclerView.Adapter<FoodAdapter.FoodViewHolder>() {

    inner class FoodViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val tvFoodName: TextView = itemView.findViewById(R.id.tv_food_name)

        init {
            tvFoodName.setOnClickListener {
                val position = adapterPosition
                if (position != RecyclerView.NO_POSITION) {
                    onItemClick.invoke(foodList[position])
                }
            }
        }
    }

    // 其他方法不变...
}
  1. 设置适配器的时候传入回调,跳转到详情页:
rvFood.adapter = FoodAdapter(foodList) { foodItem ->
    val intent = Intent(this, FoodDetailActivity::class.java)
    intent.putExtra("FOOD_ITEM", foodItem) // 因为我们的FoodItem实现了Parcelable,所以可以直接传递
    startActivity(intent)
}
  1. 在FoodDetailActivity里获取数据并展示:
class FoodDetailActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_food_detail)
        
        val foodItem = intent.getParcelableExtra<FoodItem>("FOOD_ITEM")
        foodItem?.let {
            findViewById<TextView>(R.id.tv_detail_name).text = it.name
            findViewById<TextView>(R.id.tv_detail_rate).text = "评分:${it.rate}分"
            findViewById<TextView>(R.id.tv_detail_amount).text = "价格:${it.amount}元"
            findViewById<TextView>(R.id.tv_detail_desc).text = it.desc
        }
    }
}

方式三:弹出Dialog展示详情

适合快速查看简单信息的场景,直接在点击事件里创建Dialog:

// 在适配器的ViewHolder里修改点击事件
tvFoodName.setOnClickListener {
    val position = adapterPosition
    if (position != RecyclerView.NO_POSITION) {
        val food = foodList[position]
        AlertDialog.Builder(itemView.context)
            .setTitle(food.name)
            .setMessage("评分:${food.rate}分\n价格:${food.amount}元\n${food.desc}")
            .setPositiveButton("确定") { dialog, _ -> dialog.dismiss() }
            .show()
    }
}

内容的提问来源于stack exchange,提问作者Rahul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:22:23