我正在尝试在项目中配置 Android 分页库,以将分页消息列表加载到 RecyclerView 中。由于我的 API 使用偏移量和最大值,因此我使用的是 PositionalDataSource。
这是我的 DataSource 实现,其中 DataStore 使用 RetroFit 来加载消息,我可以在控制台中看到消息正在正确加载,并转换为 MessageListItem 的实例:
class MessageDataSource: PositionalDataSource<MessageListItem>() {
override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<MessageListItem>) {
DataStore.shared.loadMessages(params.startPosition, params.loadSize) { result, error ->
if(result != null) {
callback.onResult(result.items)
} else {
callback.onError(MessageDataSourceException(error))
}
}
}
override fun loadInitial(
params: LoadInitialParams,
callback: LoadInitialCallback<MessageListItem>
) {
DataStore.shared.loadMessages(params.requestedStartPosition, params.requestedLoadSize) { response, error ->
if(response != null) {
callback.onResult(response.items, response.offset, response.total)
} else {
callback.onError(MessageDataSourceException(error))
}
}
}
}
class MessageDataSourceException(rootCause: Throwable? = null): Exception(rootCause)
这是我的 DataSourceFactory 实现:
class MessageDataSourceFactory: DataSource.Factory<Int, MessageListItem>() {
val messageLiveDataSource = MutableLiveData<MessageDataSource>()
private lateinit var messageDataSource: MessageDataSource
override fun create(): DataSource<Int, MessageListItem> {
messageDataSource = MessageDataSource()
messageLiveDataSource.postValue(messageDataSource)
return messageDataSource
}
}
这是我的 MessageListAdapter 实现:
object MessageListItemDiff: DiffUtil.ItemCallback<MessageListItem>() {
override fun areItemsTheSame(oldItem: MessageListItem, newItem: MessageListItem): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: MessageListItem, newItem: MessageListItem): Boolean {
return oldItem == newItem
}
}
class MessageListAdapter(private val clickListener: View.OnClickListener):
PagedListAdapter<MessageListItem, MessageListAdapter.MessageHolder>(MessageListItemDiff) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageHolder {
val inflatedView = LayoutInflater.from(parent.context).inflate(R.layout.item_message, parent, false)
return MessageHolder(inflatedView, clickListener)
}
override fun onBindViewHolder(holder: MessageHolder, position: Int) {
holder.bind(getItem(position)!!)
}
class MessageHolder(itemView: View, private val clickListener: View.OnClickListener) : RecyclerView.ViewHolder(itemView) {
val unreadIndicator = itemView.findViewById<ImageView>(R.id.unreadIndicator)
val title = itemView.findViewById<TextView>(R.id.title)
val dateSent = itemView.findViewById<TextView>(R.id.dateSent)
val cardView = itemView.findViewById<CardView>(R.id.card_view)
fun bind(message: MessageListItem) {
cardView.tag = message
cardView.setOnClickListener(clickListener)
title.text = message.title
dateSent.text = TimeAgo.using(message.dateSent.time)
if(message.isRead) {
unreadIndicator.setImageResource(0)
} else {
unreadIndicator.setImageResource(R.drawable.ic_unread)
}
}
}
}
最后是我的 ViewModel:
class MessageListViewModel: ViewModel() {
val messagePagedList: LiveData<PagedList<MessageListItem>>
val liveDataSource: LiveData<MessageDataSource>
init {
val messageDataSourceFactory = MessageDataSourceFactory()
liveDataSource = messageDataSourceFactory.messageLiveDataSource
val pagedListConfig = PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPageSize(30)
.setPrefetchDistance(90)
.build()
messagePagedList = LivePagedListBuilder(messageDataSourceFactory, pagedListConfig).build()
}
}
这里是 fragment 中的 onViewCreated 实现,它应该显示名为 messageList 的回收器 View :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
messageList.layoutManager = LinearLayoutManager(context!!)
messageList.setHasFixedSize(true)
messageListViewModel = ViewModelProvider(this).get(MessageListViewModel::class.java)
messageListAdapter = MessageListAdapter(this)
messageListViewModel.messagePagedList.observe(this, Observer { messages ->
messageListAdapter.submitList(messages)
})
messageList.adapter = messageListAdapter
}
问题是我可以看到数据正在从服务器加载,但它从未到达回收器 View 。如果我在观察者行 (messageListAdapter.submitList(messages)
) 上添加断点,我会收到一个带有空消息列表的调用,仅此而已。
我必须承认我真的对所有这些类以及它们应该做什么感到困惑,这是我在 Android 中的第一个分页实现,我不得不调整我在这里和那里找到的代码,因为我不想使用 Room 数据库、RxJava 或 PageKeyedDataSource,大多数示例都是这样做的。
知道会发生什么吗?
最佳答案
据我所知,要使一切正常工作,PagedList
实例在由 LiveData
分派(dispatch)后必须立即预加载初始数据。 。为此,需要在 loadInitial()
时加载数据。方法返回,这意味着您需要同步执行网络调用并调用callback.onResult()
来自loadInitial()
内在方法返回之前调用方法,而不是使用回调。在那里同步执行网络调用是安全的,因为 LivePagedListBuilder
将负责调用 PagedList.Builder()
来自后台线程。
此外,错误处理实现目前几乎没有记录且不完整(在版本 2.1.1 中),因此调用最近添加的 callback.onError()
方法在很多情况下都会失败。例如,TiledPagedList
中根本没有实现版本 2.1.1 中的错误处理。 ,这是 PagedList
的类型用于PositionalDataSource
.
最后,如果您返回 loadInitial()
中列表的精确大小(就像你在这里所做的那样),然后在 loadRange()
您需要确保始终返回所请求的项目数量。如果 API 请求 30 个项目而您只返回 20 个,您的应用程序可能会崩溃。我发现的一种解决方法是,您可以用 null
填充结果列表。值,因此它始终具有请求的大小,但是您需要启用占位符。或者,不要在 loadInitial() 中返回精确的大小,列表将动态增长。
这个 API 很复杂,使用起来也很棘手,所以不要责怪自己。 Google 目前正在开发用 Kotlin 编写的新版本 3.0,有望解决旧版本的所有问题。
关于android - RecyclerView 与 Paging Library 和 PositionalDataSource 保持为空,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60154529/