android - 可在 ViewHolder 内部流动
问题描述
我又开始用 android 编程了,因为在过去的 4 年里它发生了很大的变化,我有点困惑。我正在尝试使用 ViewHolder 中的 RxJava 和 Flowable 从服务器异步加载我的数据。我正在使用第三方库来处理我在适配器MultiViewAdaptor中的视图。这是我的活页夹中的代码
class FooBinder : ItemBinder<Foo, FooBinder.ViewHolder>() {
@Inject
protected lateinit var requestFavoriteUseCase: SendFooFavoriteUseCase
@Inject
protected lateinit var compositeDisposable: CompositeDisposable
@Inject
protected lateinit var retrieveFooFavoriteCountUseCase: RetrieveFooFavoriteCountUseCase
override fun create(inflater: LayoutInflater, parent: ViewGroup) =
ViewHolder(inflater.inflate(R.layout.item_foo_content, parent, false))
override fun bind(holder: ViewHolder, item: Foo) {
holder.itemView.foo_content_creator.text = with(item.creator) { "$firstName $lastName" }
GlideApp.with(holder.itemView)
.load(item.creator.avatar)
.dontAnimate()
.placeholder(R.drawable.ic_action_person)
.error(R.drawable.ic_action_person)
.into(holder.itemView.foo_content_profile)
retrieveFooFavoriteCountUseCase
.execute(item.id)
.applyComputationScheduler()
.subscribe { count ->
holder.itemView.foo_content_like_count.text = "$count"
}
.addTo(compositeDisposable)
}
inner class ViewHolder(view: View) : ViewHolder<Foo>(view) {
private var favorite = false
init {
view
.foo_content_button_layout
.clicks()
.flatMap {
favorite = !favorite
requestFavoriteUseCase.execute(FavoriteVM(item.id, favorite)).toObservable()
}
.subscribe {
view.foo_content_like_count.text = it.toString()
var res = R.drawable.ic_action_like_default
if (favorite) {
res = R.drawable.ic_action_like_enabled
}
view.foo_content_like_icon.setImageResource(res)
}
.addTo(compositeDisposable)
}
}
}
如您所见,我必须在不理想的绑定方法中调用服务器,并且每次用户滚动时都会调用它,我也无法取消订阅它们,直到 Activity 被销毁并调用compositeDisposable这意味着我将为一个视图提供多个一次性:(
我的问题是,如何在 ViewHolder 中使用 observable 并在屏幕中未显示时停止喂它?
解决方案
你能做的最好的事情是:
- 依靠
onViewAttachedToWindow
并onViewDetachedToWindow
控制您的订阅生命周期(订阅/处置)。 - 创建/检索您的可观察对象
onBindViewHolder
- 在父视图(片段/活动)中取消您的回收器上的适配器以强制适配器调用
onViewDetachedToWindow
所有窗口以确保您没有泄漏任何内容(例如片段,onDestoryView
调用中list.adapter = null
) - 添加
delaySubscription
(例如 400 毫秒)到您的 observables 以减少快速滚动时的滞后(当列表在动画上滑动时,您不想订阅任何内容) - 确保你的 observables 的第一次发射是同步的,这样缓存的数据可以在屏幕旋转后布局之前传递。这样您将避免在旋转过程中出现动画故障,并且 Android 将能够正确恢复 View 状态。
第 5 点可能有点令人困惑,因此有一些资源可以更广泛地解释这一点:
所以基本上同步发射意味着在订阅方法返回之前订阅可观察的项目将被发射。例如,这种行为可以通过在缓存后不修改订阅发射线程的链中的某种缓存(如 replay(1)、startWith() 等)来实现。
例子:
Observable.create(2)
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.startWith(0)
.subscribe(println(it))
println("after subscribe")
假设我们订阅了一个主线程。在这种情况下,我们将打印:
0
after subscribe
2
如您所见,即使这两个项目都将在主线程上发出,其中一个项目将在订阅时立即到达,而第二个项目将发布在循环器上。
推荐阅读
- android - Android 通过 ACTION_GET_CONTENT 仅列出具有特定扩展名的文件
- asp.net-core - ASP.Net 核心。检测是否在启动时为 https 配置?
- javascript - 将 JS 函数与 for 循环同步
- java - 从 Firebaase 检索第二个项目时出错
- c# - CSHARP 每 X 秒运行一次,但直到 bool 为真
- javascript - 使用 d3.js 的螺旋文本
- r - 将数据帧从长格式转换为宽格式,R中有很多列
- python - 将 xarray.Dataset 的字典条目转换为 numpy 数组(用于 xy 坐标等高线绘图)
- sql - SQL,我如何创建一个飞行扇区路线
- javascript - aurelia 中 repeat.for 的绑定问题