Обновите отдельный элемент в PagedList с помощью библиотеки подкачки Google - PullRequest
0 голосов
/ 18 февраля 2020

У меня есть простой список покемонов в RecyclerView с только именем покемона и "Favorite" ToggleButton. Я использую библиотеку подкачки из Android JetPack с PageKeyedDataSource для извлечения небольших фрагментов Pokemon и отображения их пользователю. Я только хочу, чтобы данные сохранялись до тех пор, пока действие не будет уничтожено (т.е. я НЕ хочу сохранять данные в комнате или базе данных, а хочу, чтобы они сохранялись до тех пор, пока ViewModel жива).

Снимок экрана приложения

Я хочу иметь возможность нажать кнопку сердца на любом элементе покемонов и обновить поле "isFavorite" в модели SimplePokemon до true или false. Насколько я понимаю, если бы я захотел изменить один элемент из этого PagedList, мне нужно было бы сделать недействительным DataSource, и это должно теоретически сгенерировать новые LiveData PagedList, которые можно было бы подать в адаптер и показать на экране.

Вопрос: Как я могу обновить отдельный элемент из PagedList с помощью библиотеки подкачки без необходимости использования комнаты или какой-либо другой базы данных?

В будущем я хочу масштабировать это решение для фида в социальных сетях, где пользователям могут нравиться посты, но я не знаю, необходимо ли (или эффективно) хранить элементы фидов социальных сетей в базе данных, такой как 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 в бэк-энде, но я просто стараюсь сделать вопрос простым.

Любая помощь будет искренне оценена.

...