开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情
继续《Android编程权威指南》第 24 章,本章应用叫 PhotoGallery,现在开始继续完善应用啦!
四、应对设备配置改变
目前的进度呢,网络请求回来的数据已经搞定了 JSON 数据解析,从前面的打印日志也是可以看出来了。然后呢,当设备发生旋转的时候,又出现了问题了,每次旋转都会发出一个新的网络请求,原因就在于 PhotoGalleryFragment 在设备旋转时会被销毁再重建,这其实还挺浪费资源的,作为用户当然也是不可以容忍的。
显然,本章的内容就是要解决旋转设备多次请求的问题。
「其实我们实际应用大多都会禁用旋转的啦,不过也避免不了少部分应用没有禁用,那没有禁用就得多去适配一下设备旋转页面销毁重建的数据问题啦」
我们现在需要的效果呢,就是在 fragment 首页创建显示发起一次网络请求得到数据,在设备配置发生改变的时候,缓存好数据,然后感知 fragment 生命周期变化,在需要的时候时候直接获取缓存好的数据显示,而不是重新发起新的网络请求。
现在呢,解决方案就是使用 ViewModel。关于 ViewModel,前面的章节也有介绍。
ViewModel 概况:developer.android.com/topic/libra…
- 首先,添加好 ViewModel 的依赖:
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
- 新建 PhotoGalleryViewModel.kt
class PhotoGalleryViewModel : ViewModel() {
val galleryItemLiveData: LiveData<List<GalleryItem>>
init {
galleryItemLiveData = FlickrFetchr().fetchPhotos()
}
}
- 更新 PhotoGalleryFragment 类,获取 PhotoGalleryViewModel 对象。
这里我们获取 ViewModel 对象的时候做点修改,再加入一个依赖:
implementation("androidx.fragment:fragment-ktx:1.5.4")
此依赖提供了一系列的扩展程序帮助我们简化 Fragment API,语法糖级别了,很好用儿,这样我们在获取 ViewModel 对象就可以这么写:
private val viewModel: PhotoGalleryViewModel by viewModels()
ViewModel 的官方文档也有说明:ViewModel 允许数据在 ViewModel 持有的状态和 ViewModel 触发的操作结束后继续存在。这种缓存意味着在常见的配置更改(例如屏幕旋转)完成后,您无需重新提取数据。
因此在 PhotoGalleryFragment 类中,我们只需要把请求网络数据的调用换成使用viewModel就行了。在 onViewCreated 中新增代码:
viewModel.galleryItemLiveData.observe(viewLifecycleOwner,
Observer { gallerayItems ->
Log.d(TAG, "Response received:$gallerayItems")
})
现在再运行项目,旋转设备,就不会再发生多次网络请求啦!
五、在RecyclerView里显示结果
本章最后的任务了,在 RecyclerView 把图片显示出来,先照着敲一遍代码后,我们后续再来优化 RecyclerView 使用,主要有各种库封装好了,特好用。先将书中任务完成再来扩展。
- 先定义 ViewHolder:
private class PhotoHolder(itemTextView: TextView) : RecyclerView.ViewHolder(itemTextView) {
val bindTitle: (CharSequence) -> Unit = itemTextView::setText
}
- 接下来添加一个 RecyclerView.Adapter:
private class PhotoAdapter(private val galleryItems:List<GalleryItem>):RecyclerView.Adapter<PhotoHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoHolder {
val textView = TextView(parent.context)
return PhotoHolder(textView)
}
override fun onBindViewHolder(holder: PhotoHolder, position: Int) {
val galleryItem = galleryItems[position]
holder.bindTitle(galleryItem.title)
}
override fun getItemCount(): Int {
return galleryItems.size
}
}
- 最后就是给 adapter 关联一下数据:
photoRecyclerView.adapter = PhotoAdapter(gallerayItems)
- 好啦,运行起来:
发现一个问题,这里 viewModel 没被调用,所以网络请求是不会被调用的,它只有监听,并没有被初始化使用,Kotlin 中的 by 是一种委托模式,可以延迟初始化加载,这里示例代码的写法其实并不适用,所以最终还是不用 fragment 中的扩展去获取 viewModel 啦,改为书中的方式。在 onCreate 方法中初始化 viewModel:
viewModel = ViewModelProvider(this)[PhotoGalleryViewModel::class.java]
再次运行:
不过由于网络问题,数据一直请求失败,可能哪里还有小问题,所以就没有运行效果啦,不过今天内容大体上还是学完啦。
好啦,优化了一下,补下效果图。
其他
PhotoGallery 项目 Demo 地址:
🌈关注我吖~❤️
公众号:妮K妮K妮