我在 RecyclerView 中有一个简单的口袋妖怪列表,只有口袋妖怪的名字和一个“最喜欢的”切换按钮。我正在使用带有 PageKeyedDataSource 的 Android JetPack 的分页库来检索小块 Pokemon 并将它们显示给用户。只要 Activity 没有被破坏,我只希望数据保持不变(即我不想将数据保持到 Room 或数据库,而是只要 ViewModel 还活着就让它保持不变)。
我希望能够单击任何 Pokemon 项目上的心形按钮并将 SimplePokemon 模型中的“isFavorite”字段更新为true
or false
。据我了解,如果我想更改该 PagedList 中的单个项目,我需要使 DataSource 无效,并且理论上应该生成一个 PagedList 的新 LiveData,该 LiveData 可以提供给 Adapter 并显示在屏幕上。
问题:如何在不需要 Room 或其他数据库的情况下使用分页库更新 PagedList 中的单个项目?
将来,我想将此解决方案扩展到用户可以喜欢帖子的社交媒体提要,但我不知道是否有必要(或有效)将社交提要项目存储在诸如 Room 之类的数据库中,因为这些提要项目不断变化。所以我选择将它们存储在 ViewModel 中,然后在每次用户退出应用程序时清除它们。
到目前为止,这是我的代码:
简单口袋妖怪.kt:
data class SimplePokemon(
@SerializedName("name") val name: String,
@SerializedName("url") val url: String,
var isFavorite: Boolean = false
)
PokemonViewModel.kt:
class PokemonViewModel(application: Application) : AndroidViewModel(application) {
private val config = PagedList.Config.Builder()
.setPageSize(20)
.setEnablePlaceholders(false)
.build()
private fun initializedPagedListBuilder(config: PagedList.Config): LivePagedListBuilder<String, SimplePokemon> {
val dataSourceFactory = object : DataSource.Factory<String, SimplePokemon>() {
override fun create(): DataSource<String, SimplePokemon> {
return PokemonDataSource()
}
}
return LivePagedListBuilder<String, SimplePokemon>(dataSourceFactory, config)
}
fun pokemonPagedListLiveData(): LiveData<PagedList<SimplePokemon>> {
return initializedPagedListBuilder(config).build()
}
}
口袋妖怪适配器.kt:
class PokemonAdapter :
PagedListAdapter<SimplePokemon, PokemonAdapter.PokemonViewHolder>(PokemonDiffUtil()) {
inner class PokemonViewHolder(v: View) : RecyclerView.ViewHolder(v) {
private val pokemonNameTextView: TextView = v.findViewById(R.id.pokemon_name_text_view)
private val pokemonFavoriteToggle: ToggleButton =
v.findViewById(R.id.pokemon_favorite_toggle_button)
fun bind(data: SimplePokemon) {
pokemonNameTextView.text = data.name
pokemonFavoriteToggle.isChecked = data.isFavorite
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PokemonViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.item_simple_pokemon, parent, false)
return PokemonViewHolder(view)
}
override fun onBindViewHolder(holder: PokemonViewHolder, position: Int) {
val item = getItem(position)
item?.let { holder.bind(it) }
}
}
PokemonDataSource.kt:
class PokemonDataSource : PageKeyedDataSource<String, SimplePokemon>() {
private val api = NetworkService.pokemonNetworkInterface
override fun loadInitial(
params: LoadInitialParams<String>,
callback: LoadInitialCallback<String, SimplePokemon>
) {
api.getPokemon().enqueue(object : Callback<PokeResponse<List<SimplePokemon>>> {
override fun onFailure(call: Call<PokeResponse<List<SimplePokemon>>>?, t: Throwable?) {
Log.e("PokemonDataSource", "Failed to fetch data!")
}
override fun onResponse(
call: Call<PokeResponse<List<SimplePokemon>>>?,
response: Response<PokeResponse<List<SimplePokemon>>>
) {
val listing = response.body()
val pokemon = listing?.results
callback.onResult(pokemon ?: listOf(), listing?.previous, listing?.next)
}
})
}
override fun loadAfter(
params: LoadParams<String>,
callback: LoadCallback<String, SimplePokemon>
) {
api.getPokemon(url = params.key)
.enqueue(object : Callback<PokeResponse<List<SimplePokemon>>> {
override fun onFailure(
call: Call<PokeResponse<List<SimplePokemon>>>?,
t: Throwable?
) {
Log.e("PokemonDataSource", "Failed to fetch data! Oh Noooo!")
}
override fun onResponse(
call: Call<PokeResponse<List<SimplePokemon>>>?,
response: Response<PokeResponse<List<SimplePokemon>>>
) {
val listing = response.body()
val pokemon = listing?.results
callback.onResult(pokemon ?: listOf(), listing?.next)
}
})
}
override fun loadBefore(
params: LoadParams<String>,
callback: LoadCallback<String, SimplePokemon>
) {
api.getPokemon(url = params.key)
.enqueue(object : Callback<PokeResponse<List<SimplePokemon>>> {
override fun onFailure(
call: Call<PokeResponse<List<SimplePokemon>>>?,
t: Throwable?
) {
Log.e("PokemonDataSource", "Failed to fetch data! Oh Noooo!")
}
override fun onResponse(
call: Call<PokeResponse<List<SimplePokemon>>>?,
response: Response<PokeResponse<List<SimplePokemon>>>
) {
val listing = response.body()
val pokemon = listing?.results
callback.onResult(pokemon ?: listOf(), listing?.previous)
}
})
}
我还想确保每次更新 DataSource 时 RecyclerView 都不会跳到顶部。
理想的情况是,只要 Activity 处于活动状态并能够在本地更新单个 Pokemon 项目,就保留一个 Pokemon 列表。从理论上讲,我还会向后端发送一个 POST 请求以更新后端的口袋妖怪,但我只是想让问题保持简单。
任何帮助将不胜感激。