Android入门到实战(八):从发现页到详情页——跳转、传值与RecyclerView多类型布局

一. 引言

在上一篇文章里,我们从零开始实现了 App 的 发现页面,通过网络请求获取数据,并使用 RecyclerView 展示了剧集列表。

但光有发现页还不够,用户在点击一部剧时,自然希望进入到一个更详细的页面,去查看它的简介、标签以及剧集列表。本篇我们就来实现 发现详情页

主要包含以下内容:

  1. 从发现页跳转到详情页(Activity 跳转与传值)
  2. 详情页的 UI 布局(背景、Toolbar、RecyclerView)
  3. RecyclerView 多类型布局(头部 + 剧集列表)
  4. ViewModel + LiveData 数据驱动(自动刷新 UI)

通过这一篇,你将掌握 Android 开发中常见的"跳转 → 数据传递 → 多类型列表 → 数据绑定"的完整流程。

二. 从发现页跳转到详情页

2.1 发送跳转

在发现页的 Adapter 中,我们可以为每一个剧集的 Item 添加点击事件,然后通过 Intent 启动 DiscoverDetailActivity,并把 DiscoverDrama 对象传递过去:

Kotlin 复制代码
val intent = Intent(context, DiscoverDetailActivity::class.java)
intent.putExtra("drama", drama) // drama 是 DiscoverDrama 类型
context.startActivity(intent)

这里我们用到了 putExtra,因为 DiscoverDrama 已经实现了 Serializable,所以可以直接传递。

2.2 接收参数

在 DiscoverDetailActivity 中,通过 intent.getSerializableExtra 来接收数据:

Kotlin 复制代码
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private fun initData() {
    discoverDrama = intent.getSerializableExtra("drama", DiscoverDrama::class.java)
}

这样我们就能在详情页中拿到用户点击的剧集信息,并用于后续的 UI 展示和数据请求。

三. 详情页整体布局概览

在详情页,我们主要分为三个部分:

1. 背景与 Toolbar

  • 页面顶部是一个渐变背景 (View) 和透明的 MaterialToolbar,用于展示标题"剧集详情"。
  • 使用 enableEdgeToEdge() 和 WindowInsetsCompat 处理状态栏高度,让内容贴合屏幕边缘。

2. RecyclerView

占据主体区域,用于展示两类内容:

  1. 头部信息:封面、标题、描述、标签、词汇量
  2. 剧集列表:每一集的标题、文件大小、下载状态等

3. 布局特点

  • RecyclerView 采用 LinearLayoutManager 垂直排列。
  • 头部视图与列表项通过 Adapter 的 getItemViewType 区分,实现多类型布局。
  • 数据完全通过 ViewModel + LiveData 绑定到 RecyclerView,无需在 Activity 中手动更新视图。

这种布局方式简洁而高效,既能展示剧集的详细信息,也便于扩展后续功能(例如下载按钮或播放按钮)。

四. RecyclerView 多类型布局实现

发现详情页中,我们的 RecyclerView 既要展示 头部信息 ,又要展示 剧集列表 。为此,我们采用 多类型布局的方式,实现两类 ViewHolder:

4.1 Adapter 设计
Kotlin 复制代码
class DiscoverDetailAdapter(
    private val discoverDrama: DiscoverDrama
): RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    companion object {
        const val TYPE_HEADER = 0
        const val TYPE_CONTENT = 1
    }

    private var episodes: List<DiscoverEpisode> = emptyList()

    override fun getItemViewType(position: Int): Int {
        return if (position == 0) TYPE_HEADER else TYPE_CONTENT
    }

    override fun getItemCount(): Int = episodes.size + 1
}
  • 第一个位置 (position == 0) 是 头部视图
  • 其余位置为 剧集列表
  • getItemCount() 返回 episodes.size + 1,因为头部占一行
4.2 ViewHolder 绑定数据

头部视图 (HeaderViewHolder)

  • 显示剧封面、标题、描述、标签、词汇量
  • 使用 Glide 加载封面图片
  • 标签动态生成 TextView 并添加到 LinearLayout
Kotlin 复制代码
class HeaderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    private val cover = itemView.findViewById<ImageView>(R.id.ivCover)
    private val title = itemView.findViewById<TextView>(R.id.tvTitle)
    private val desc = itemView.findViewById<TextView>(R.id.tvDesc)
    private val wordCount = itemView.findViewById<TextView>(R.id.tvVocab)
    private val tagContainer = itemView.findViewById<LinearLayout>(R.id.tagContainer)

    fun bindData(drama: DiscoverDrama) {
        Glide.with(itemView.context).load(drama.realCoverUrl).into(cover)
        title.text = drama.title
        desc.text = drama.description
        wordCount.text = "词汇量: ${drama.vocabularyCount ?: 0}"
        tagContainer.removeAllViews()
        drama.tags?.split(",")?.forEach { tag ->
            val tv = TextView(itemView.context).apply {
                text = tag
                // 背景、圆角、透明度等样式
            }
            tagContainer.addView(tv)
        }
    }
}

剧集列表视图 (EpisodeViewHolder)

  • 显示剧集标题、文件大小、下载状态
  • 预留下载逻辑和进度条
Kotlin 复制代码
class EpisodeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    private val title: TextView = itemView.findViewById(R.id.episodeTitle)
    private val size: TextView = itemView.findViewById(R.id.episodeSize)
    private val statusIcon: ImageView = itemView.findViewById(R.id.statusIcon)
    private val statusProgress: ProgressBar = itemView.findViewById(R.id.statusProgress)

    fun bindData(episode: DiscoverEpisode) {
        title.text = "${episode.index}. ${episode.title}"
        size.text = episode.fileSize ?: ""
        // 下载状态逻辑可在此扩展
    }
}
4.3 数据更新
  • 通过 ViewModel 获取剧集列表数据
  • 使用 LiveData 观察数据变化,并调用 Adapter 的 setEpisodes() 更新 RecyclerView
Kotlin 复制代码
viewModel.episodes.observe(this) { episodes ->
    adapter.setEpisodes(episodes)
}

这样实现了 Activity 不直接操作 RecyclerView 的思想,保证了 UI 与数据的分离。

五. 数据获取与绑定流程

在详情页中,剧集列表的数据来源于网络请求。为了实现 UI 与数据分离 ,我们采用 ViewModel + LiveData 的方式管理数据。

5.1 ViewModel 请求数据

DiscoverDetailViewModel 负责请求剧集列表,并将结果通过 LiveData 暴露给 UI:

Kotlin 复制代码
class DiscoverDetailViewModel : ViewModel() {

    val episodes = MutableLiveData<List<DiscoverEpisode>>()
    val isLoading = MutableLiveData<Boolean>()

    private val discoverDramaRepository by lazy { DiscoverRespository() }

    fun fetchEpisodes(drama: DiscoverDrama) {
        viewModelScope.launch {
            isLoading.value = true
            val result = discoverDramaRepository.fetchEpisodes(drama)
            result.onSuccess {
                println("获取剧集 ${drama.title} 的集列表成功: ${it.size} 条数据")
                episodes.value = it
            }.onFailure {
                episodes.value = emptyList()
            }
            isLoading.value = false
        }
    }
}
  • viewModelScope.launch 在协程中发起网络请求,保证不会阻塞 UI 线程
  • 成功时,将数据赋值给 episodes LiveData
  • 失败时,清空列表,保证 RecyclerView 安全更新
5.2 Activity 观察数据

在 DiscoverDetailActivity 中,RecyclerView Adapter 不直接请求数据,而是 观察 LiveData

Kotlin 复制代码
viewModel.episodes.observe(this) { episodes ->
    adapter.setEpisodes(episodes)
    Log.d("DiscoverDetailActivity", "Episodes updated: ${episodes.size} items")
}
  • 当 LiveData 更新时,Adapter 自动刷新 RecyclerView
  • Activity 只负责 UI 初始化和 LiveData 绑定,无需手动刷新列表
5.3 请求与展示流程总结
  1. Activity 启动后,通过 Intent 获取 DiscoverDrama 参数
  2. 调用 viewModel.fetchEpisodes(drama) 发起网络请求
  3. ViewModel 请求成功后,将数据赋值给 LiveData
  4. Activity 观察 LiveData,并将数据传递给 Adapter
  5. Adapter 更新 RecyclerView,实现 UI 自动刷新

六.运行效果与总结

6.1 最终效果展示
  • 用户在 发现页面 点击某部剧集
  • 页面跳转到 详情页
  • 页面顶部展示剧的封面、标题、描述、标签和词汇量
  • 下方 RecyclerView 展示剧集列表,每一集显示标题、文件大小和下载状态(可扩展)
  • UI 完全响应 LiveData 数据更新,无需手动刷新
6.2 本篇收获

通过这一篇文章,我们掌握了:

Activity 跳转与参数传递

  • 使用 Intent 传递 Serializable 对象
  • 在目标 Activity 中安全接收数据

RecyclerView 多类型布局

  • 头部视图 + 列表视图
  • Adapter 分类型管理 ViewHolder

ViewModel + LiveData 数据驱动 UI

  • Activity 不直接操作数据
  • RecyclerView 自动响应数据变化

这种模式不仅使代码清晰、可维护,还符合 Android 架构最佳实践。

相关推荐
我想_iwant9 小时前
android集成unity后动态导入 assetsBundle
android·unity·游戏引擎
IAM四十二13 小时前
Android Json 解析你还在用 fastjson 吗?
android·json·fastjson
alexhilton13 小时前
Android ViewModel数据加载:基于Flow架构的最佳实践
android·kotlin·android jetpack
小阳睡不醒16 小时前
小白成长之路-develops -jenkins部署lnmp平台
android·运维·jenkins
踏雪羽翼17 小时前
Android 接入deepseek
android
whatever who cares17 小时前
Android/Java 异常捕获
android·java·开发语言
火车叼位17 小时前
Realm数据库Schema迁移终极指南:从入门到生产环境
android