web-dev-qa-db-ja.com

Android Paging ArchitectureライブラリのPagedListの変更

現在、ページングアーキテクチャライブラリ(バージョン2.1.0-beta01執筆時点で)私のアプリに。 1つのコンポーネントは、ユーザーが個々のアイテムを削除できるリストです。このリストはネットワークのみであり、Roomでローカルにキャッシュすることは意味がありません。

PagedListは不変であり、変更をサポートしません。私はリストのコピーを持っていることを読んだことがありますが、それは修正され、新しいリストが返されるので返されます。ドキュメントには同じことが記載されています:

ネットワークAPIがリスト内の1つのアイテムの更新を通知するなど、より詳細な更新シグナルがある場合は、ネットワークからメモリにデータをロードすることをお勧めします。次に、インメモリスナップショットをラップするDataSourceを介して、そのデータをPagedListに提示します。メモリ内コピーが変更されるたびに、以前のデータソースを無効にし、スナップショットの新しい状態をラップする新しいデータソースを作成できます。

現在、簡単なリストを表示するための基本的な推奨実装があります。私のDataSourceは次のようになります。

class MyDataSource<SomeItem> : PageKeyedDataSource<Int, SomeItem>() {

    override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, SomeItem>) {
        // Simple load from API and notification of `callback`.
    }

    override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, SomeItem>) {
        // Simple load from API and notification of `callback`.
    }

    override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, SomeItem>) {
        // Simple load from API and notification of `callback`.
    }
}

ドキュメントで参照されているメモリ内キャッシュの具体的な実装(ルームなし、データセット全体の無効化なし)はどのようになりますか?

12
rubengees

DataSourceは不変データを保持することを意図しているという点で正しいです。これは、Room and Paging Libraryがより多くの意見を述べた設計上の決定を行い、不変データを提唱しようとしているためだと思います。

そのため、公式ドキュメントには、データセットを更新または変更するセクションがあり、そのような変更が発生したときにデータソースを無効にする必要があります。

ページングされたデータの更新:リスト内の単一のアイテムへの更新を通知するネットワークAPIなど、より詳細な更新信号がある場合は、ロードすることをお勧めしますネットワークからメモリへのデータ。次に、インメモリスナップショットをラップするDataSourceを介して、そのデータをPagedListに提示します。メモリ内コピーが変更されるたびに、以前のデータソースを無効にし、スナップショットの新しい状態をラップする新しいデータソースを作成できます。

ソース: https://developer.Android.com/reference/Android/Arch/paging/DataSource


それを念頭に置いて、私はあなたが説明した問題をいくつかのステップを使用して解決することが可能であると信じています。

これは2つのステップを含むため、最もクリーンな方法ではない場合があります。

PagedListが保持しているスナップショットの参照を取得できます。これはMutableList型です。次に、データソースを無効にすることなく、そのスナップショット内のアイテムを削除または更新することができます。

次に、ステップ2はnotifyItemRemoved(index)notifyItemChanged(index)のようなものを呼び出すことです。

DataSourceに変更をオブザーバーに強制的に通知することはできないため、手動で変更する必要があります。

pagedList.snapshot().remove(index) // Removes item from the pagedList
adapter.notifyItemRemoved(index) // Triggers recyclerview to redraw/rebind to account for the deleted item.

あなたのDataSource.Factoryにもっと良い解決策が見つかるかもしれません。公式ドキュメントによると、DataSource.Factoryは、データが更新されたときに新しいPagedListを発行するものでなければなりません。

ページングされたデータの更新:更新を提供するソースからデータをページングするには、DataSource.Factoryを作成できます。作成された各DataSourceは、現在のスナップショットを無効にするデータセットの更新が発生します。たとえば、データベースからクエリをページングするときに、クエリ対象のテーブルがアイテムを挿入または削除します。 DataSource.Factoryを使用して、ネットワークページリストの複数のバージョンを提供することもできます。データの新しいバージョンを取得するためにすべてのコンテンツをリロードする(たとえば、スワイプして更新するなどのアクションに応じて)必要がある場合は、明示的な更新信号を接続して、現在のDataSourceでinvalidate()を呼び出すことができます。

ソース: https://developer.Android.com/reference/Android/Arch/paging/DataSource

ただし、この2番目のアプローチに適したソリューションは見つかりませんでした。

4
Felipe Roriz

データレイヤーに到達しないでリストを変更する場合は、アダプターのsubmitListをオーバーライドしてから、PagedListオブジェクトにコールバックを設定する必要があります。 PagedListが変更されるたびに、それらの変更をローカルデータセットにコピーできます。これは推奨されませんが、動作させるための最小限のハックです。

以下に例を示します。

class MyListAdapter : PagedListAdapter<MyDataItem, MyViewHolder>(MyDiffCallback()) {

    /**
     * This data set is a bit of a hack -- we are copying everything the PagedList loads into our
     * own list.  That way we can modify it.  The docs say you should go all the way down to the
     * data source, modify it there, and then bubble back up, but I don't think that will actually
     * work for us when the changes are coming from the UI itself.
     */
    private val dataSet = arrayListOf<MyDataItem>()

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        //Forces the next page to load when we reach the bottom of the list
        getItem(position)

        dataSet.getOrNull(position)?.let {
            holder.populateFrom(it)
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val view = parent.inflate(R.layout.my_view_holder)
        return MyViewHolder(view)
    }

    class MyDiffCallback : DiffUtil.ItemCallback<MyDataItem>() {

        override fun areItemsTheSame(oldItem: MyDataItem, newItem: MyDataItem) =
                oldItem.id == newItem.id

        override fun areContentsTheSame(oldItem: MyDataItem, newItem: MyDataItem) =
                oldItem == newItem
    }

    override fun submitList(pagedList: PagedList<MyDataItem>?) {
        pagedList?.addWeakCallback(listOf(), object : PagedList.Callback() {
            override fun onChanged(position: Int, count: Int) {
                dataSet.clear()
                dataSet.addAll(pagedList)
            }

            override fun onInserted(position: Int, count: Int) {
                dataSet.clear()
                dataSet.addAll(pagedList)
            }

            override fun onRemoved(position: Int, count: Int) {
                dataSet.clear()
                dataSet.addAll(pagedList)
            }
        })
        super.submitList(pagedList)
    }
}
1