尝试制作一个文件下载管理器
我们有一个包含回收站视图的活动,回收站视图中的每个项目代表一个文件,该文件处于下载或下载状态。每个回收器视图项都有一个回调,这个回调有 3 个信息:onSuccess (file)、onError(Error)、progress(Int)。 现在我想在 BindViewHolder 中提取此信息以显示进度,但我无法在回调中获取值: (为简单起见删除其他代码)
class DownloadActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val list = mutableListOf<DItem>()
list.add(DItem("url", callback = object : Listener{
override fun onSuccess(file: File) {
}
override fun onFailure(error: Error) {
}
override fun onProgress(progress: Int) {
}
})
// trigger from here for simplicity otherwise each list item has a download button which will trigger this:
FileDownloadManager().download("utl", list[0].callback)
}
// adapter
class DownloadAdapter : RecyclerView.Adapter<MyViewhOdler>() {
val list = mutableListOf<DItem>()
override fun onBindViewHolder(holder: MyViewhOdler, position: Int) {
// how to get value here:
list.get(0).callback
}
}
class FileDownloadManager {
override fun download(url: String, callback: Listener) {
// on donwloading start, will trigger
// callback.progress(<some value>)
}
interface Listener{
fun onSuccess(file: File)
fun onFailure(error: Error)
fun onProgress(progress: Int)
}
}
// Ditem
data class DItem (
var url: String? = null,
var callback: Listener,
var fcallback: ((File, Error, Int)-> Unit)
)
}
如果我没理解错的话,你想获取“onBindViewHolder()”中的“progress”值,但你不知道该怎么做。
我想到的第一件事是:不修改太多代码但也可能是一个肮脏的解决方案:
您实际上稍后定义了您的对象回调,因此构造函数将不再接受“回调”,而是您稍后将初始化的 var。
class DItem(
var url: String? = null,
var fcallback: ((File, Error, Int) -> Unit)
) {
lateinit var callback: FileDownloadManager.Listener
}
此时,在“onBindViewHolder()”中,您现在可以定义回调并在该位置获取值:
fun onBindViewHolder() {
println("onBindViewHolder")
list[0].callback = object : FileDownloadManager.Listener {
override fun onSuccess(file: File) {
}
override fun onFailure(error: Error) {
}
override fun onProgress(progress: Int) {
print("progress: $progress")
}
}
}
我希望我确实正确理解了这个问题,我真的想强调这样一个事实,即这不一定是一个干净的解决方案,但却是实施速度最快且代码重构量最少的解决方案之一。
我认为你的问题源于这样一个事实,即你在你的适配器中有一个具有复杂行为的domain模型列表,它应该是非常愚蠢的view模型,它只代表每个列表项的当前状态。
onBindViewHolder
只是为了将给定位置的项目的当前状态拍打到同一位置的匹配视图。它不应该坚持回调或试图更新自己。
您可以尝试将您的域模型包装在一个对象中,该对象保持当前的进度状态,然后通知拥有的适配器该刷新了。
例如:
这是适配器中的物品。 它将包装数据模型并公开当前状态以绑定到视图。
class ListItem(val url : String) {
val currentProgress = MutableLiveData<Int>(0)
val dItem = DItem(url, object : callback {
fun onProgress(int progress) {
currentProgress.postValue(progress)
}
// ...
// Store file or error from other functions as well
}
}
然后你的activity创建这些对象的列表而不是DItem,观察状态变化,当它发生变化时通知adapter那个索引需要刷新:
class DownloadActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val list = mutableListOf<ListItem>()
list.add(ListItem("url").apply {
currentProgress.observe(this@DownloadActivity) {
// Simple implementation just refreshes the whole list when one progress changes
// Exercise for the reader to pass along the specific index to mark as change
adapter.notifyDataChanged()
}
})
然后你的 ViewHolder 只关心委托按钮点击事件和将视图设置为当前状态:
override fun onBindViewHolder(holder: MyViewhOdler, position: Int) {
val listItem = list.get(position)
holder.buttonThatTriggersDownload.setOnClickListener {
// Set click listener to trigger download
}
holder.viewThatShowsDownloadProgress.value = listItem.currentProgress.value
}