我在RecyclerView中有一个简单的口袋妖怪列表,只列出了口袋妖怪的名字和一个“收藏夹” ToggleButton。我正在将Android JetPack的分页库与PageKeyedDataSource结合使用,以检索口袋妖怪的小块并将其显示给用户。我只希望只要不破坏Activity就可以保留数据(即,我不希望将数据保留到Room或数据库中,而只要ViewModel处于活动状态就可以保留)。
我希望能够在任何Pokemon项上单击“心形”按钮,并将SimplePokemon模型中的“ isFavorite”字段更新为true
或false
。根据我的理解,如果我想从该PagedList中更改单个项目,则需要使DataSource无效,并且从理论上讲,应该生成一个新的PagedList LiveData,可以将其馈送到适配器并显示在屏幕上。 >
问题:如何使用分页库从PagedList中更新单个项目,而无需使用Room或其他数据库?
将来,我想将此解决方案扩展到用户喜欢帖子的社交媒体供稿,但是我不知道是否有必要(或高效)将社交供稿项存储在诸如Room之类的数据库中,因为这些供稿项目在不断变化。因此,我选择将它们存储在ViewModel中,然后在每次用户退出应用程序时将其清除。
到目前为止,这是我的代码:
SimplePokemon.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()
}
}
PokemonAdapter.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)
}
})
}
我还想确保每次数据源更新时,RecyclerView不会跳到顶部。
理想的情况是,只要活动仍然存在,就可以保留一个口袋妖怪列表,并且能够在本地更新各个口袋妖怪物品。从理论上讲,我还将向后端发送一个POST请求以更新后端中的Pokemon,但我只是想使问题保持简单。
衷心感谢您的帮助。
答案 0 :(得分:0)
为了更新 PagedListAdapter 中的单个项目,我在 PagedListAdapter 中添加了这个方法。 就我而言。
// Adapter Class
public class ChatHistoryAdapter extends PagedListAdapter<ChatHistoryEntity, ChatHistoryAdapter.ViewHolder> {
OnClickListener<ChatHistoryEntity> listener;
Context mContext;
public ChatHistoryAdapter(Context context) {
super(DIFF_CALLBACK);
mContext = context;
}
public void setOnClickListener(OnClickListener<ChatHistoryEntity> listener) {
this.listener = listener;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
ItemChatHistoryBinding binding =
ItemChatHistoryBinding.inflate(layoutInflater,parent,false);
return new ViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
ChatHistoryEntity current = getChat(position);
holder.binding.tvSender.setText(current.getName_or_mobile_or_groupName());
holder.binding.tvMessage.setText(current.getSubject());
// holder.binding.tvTimestamp.setText(current.getMessage_date_time().toString());
// holder.tv_sender.setText(current.getContact_name_or_mobile_or_groupName());
// holder.tv_message.setText(current.getMessage());
if (current.isSelected()){
holder.binding.mainLayout.setBackgroundColor(ContextCompat.getColor(mContext, R.color.item_selection));
// holder.binding.mainLayout.setBackgroundColor();
}else {
holder.binding.mainLayout.setBackgroundColor(0);
}
holder.Bind(current,listener,position);
}
public ChatHistoryEntity getChat(int position) {
return getItem(position);
}
public void updateItemSelection(int position,boolean isSelect){
ChatHistoryEntity current = getChat(position);
current.setSelected(isSelect);
notifyDataSetChanged();
}
public void clearSelection(){
List<ChatHistoryEntity> collect = StreamSupport.stream(getCurrentList().snapshot())
.peek(o -> o.setSelected(false))
.collect(Collectors.toList());
// submitList(PagedList<ChatHistoryEntity> list = new PagedList<>());
notifyDataSetChanged();
}
private static final DiffUtil.ItemCallback<ChatHistoryEntity> DIFF_CALLBACK =
new DiffUtil.ItemCallback<ChatHistoryEntity>() {
@Override
public boolean areItemsTheSame(@NonNull ChatHistoryEntity oldItem,
@NonNull ChatHistoryEntity newItem) {
return oldItem.getId() == newItem.getId();
}
@Override
public boolean areContentsTheSame(@NonNull ChatHistoryEntity oldItem,
@NonNull ChatHistoryEntity newItem) {
return oldItem.getId() == newItem.getId()
&& oldItem.getName_or_mobile_or_groupName()
.equalsIgnoreCase(newItem.getName_or_mobile_or_groupName());
}
};
public static class ViewHolder extends RecyclerView.ViewHolder {
ItemChatHistoryBinding binding;
public ViewHolder(ItemChatHistoryBinding binding) {
super(binding.getRoot());
this.binding =binding;
}
void Bind(ChatHistoryEntity chatEntity, OnClickListener<ChatHistoryEntity> mOnCharacterClick,
int position) {
binding.mainLayout.setOnClickListener(v ->
mOnCharacterClick.OnClick(chatEntity,"",position));
}
}
}
// Activity or fragument Class.
public class MainActivity extends AppCompatActivity {
mAdapter.setOnClickListener((currentObj, mode, position) -> {
// This is how you update single item in PagedListAdapter list.
mAdapter.updateItemSelection(position,!currentObj.isSelected())
});
}