13

我们正在尝试使用架构组件分页库在 Leanback VerticalGridSupportFragment 中实现分页。Leanback 本身与分页库没有任何开箱即用的兼容性,因此我们扩展了它的 ObjectAdapter 类并设法很容易地实现附加清除操作,但我们很难尝试进行修改操作工作。在内容修改操作期间,分页库的 PagedList 类使用 AsyncPagedListDiffer 计算差异,该类在内部使用 PagedStorageDiffHelper 这是一个包私有类,它在内部使用 PagedList 的包私有 PagedStorage 字段来访问实际的底层数据。因此,由于可见性限制,我们无法实现与分页库内部使用的相同逻辑。我们正在寻找一种干净而巧妙的方法来使 Leanback 与 Paging 一起工作,而无需提取和修改两者的任何内部结构。这是我们的 ObjectAdapter 实现,它支持追加和清除数据,但不支持内容修改。

有没有人设法通过分页库在 Leanback 中实现分页?

class LeanbackVerticalGridPagedListAdapter<T>(
    presenter: Presenter,
    private val stubItem: T
) : ObjectAdapter(presenter) {

    private val mUpdateCallback = object : ListUpdateCallback {

        override fun onInserted(position: Int, count: Int) {
            notifyItemRangeInserted(position, count)
        }

        override fun onRemoved(position: Int, count: Int) {
            notifyItemRangeRemoved(position, count)
        }

        override fun onMoved(fromPosition: Int, toPosition: Int) {
            notifyItemMoved(fromPosition, toPosition)
        }

        override fun onChanged(position: Int, count: Int, payload: Any?) {
            notifyItemRangeChanged(position, count, payload)
        }
    }

    private var mPagedList: PagedList<T>? = null
    private var mSnapshot: PagedList<T>? = null

    private val mPagedListCallback = object : PagedList.Callback() {
        override fun onInserted(position: Int, count: Int) {
            mUpdateCallback.onInserted(position, count)
        }

        override fun onRemoved(position: Int, count: Int) {
            mUpdateCallback.onRemoved(position, count)
        }

        override fun onChanged(position: Int, count: Int) {
            mUpdateCallback.onChanged(position, count, null)
        }
    }

    override fun size(): Int =
        mPagedList?.size
            ?: mSnapshot?.size
            ?: 0

    override fun get(index: Int): T? =
        mPagedList?.let {
            it.loadAround(index)
            it[index] ?: stubItem
        } ?: mSnapshot?.let {
            it[index]
        } ?: throw IndexOutOfBoundsException("Item count is zero, getItem() call is invalid")

    fun submitList(pagedList: PagedList<T>?) {
        if (pagedList == null) {
            val removedCount = size()
            if (mPagedList != null) {
                mPagedList!!.removeWeakCallback(mPagedListCallback)
                mPagedList = null
            } else if (mSnapshot != null) {
                mSnapshot = null
            }
            // dispatch update callback after updating mPagedList/mSnapshot
            mUpdateCallback.onRemoved(0, removedCount)
            return
        }

        if (mPagedList == null && mSnapshot == null) {
            // fast simple first insert
            mPagedList = pagedList
            pagedList.addWeakCallback(null, mPagedListCallback)

            // dispatch update callback after updating mPagedList/mSnapshot
            mUpdateCallback.onInserted(0, pagedList.size)
            return
        }

        if (mPagedList != null) {
            // first update scheduled on this list, so capture mPages as a snapshot, removing
            // callbacks so we don't have resolve to updates against a moving target
            mPagedList!!.removeWeakCallback(mPagedListCallback)
            mSnapshot = mPagedList!!.snapshot() as PagedList<T>
            mPagedList = null
        }

        if (mSnapshot == null || mPagedList != null) {
            DevUtil.crashDuringDevelopment(IllegalStateException("must be in snapshot state to diff"))
        }
    }
}
4

1 回答 1

1

如果有人仍在寻找它,请通过移植PagedListAdapter让它工作:

import androidx.leanback.widget.ArrayObjectAdapter
import androidx.leanback.widget.Presenter
import androidx.leanback.widget.PresenterSelector
import androidx.paging.AsyncPagedListDiffer
import androidx.paging.PagedList
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListUpdateCallback

/**
 * Base adapter class from leanback's ArrayObjectAdapter which supports paging
 * @link androidx.paging.PagedListAdapter
 * Use
 * @link
 * #submitList
 * methods to submit PagedList returned by paging library
 */
@Suppress("unused")
class PagedArrayObjectAdapter<T> : ArrayObjectAdapter, ListUpdateCallback {

    private val logTag = PagedArrayObjectAdapter::class.java.name
    private val differ: AsyncPagedListDiffer<T>
    private val listener: AsyncPagedListDiffer.PagedListListener<T> =
        AsyncPagedListDiffer.PagedListListener { previousList, currentList ->
            @Suppress("DEPRECATION")
            onCurrentListChanged(currentList)
            onCurrentListChanged(previousList, currentList)
        }

    constructor(diffCallback: DiffUtil.ItemCallback<T>) : this(
        AsyncDifferConfig.Builder<T>(diffCallback).build()
    )

    constructor(config: AsyncDifferConfig<T>) : super() {
        differ = AsyncPagedListDiffer(this, config)
        differ.addPagedListListener(listener)
    }

    constructor(
        presenter: Presenter,
        diffCallback: DiffUtil.ItemCallback<T>
    ) : this(
        presenter,
        AsyncDifferConfig.Builder<T>(diffCallback).build()
    )

    constructor(
        presenter: Presenter,
        config: AsyncDifferConfig<T>
    ) : super(presenter) {
        differ = AsyncPagedListDiffer(this, config)
        differ.addPagedListListener(listener)
    }

    constructor(
        presenterSelector: PresenterSelector,
        diffCallback: DiffUtil.ItemCallback<T>
    ) : this(
        presenterSelector,
        AsyncDifferConfig.Builder<T>(diffCallback).build()
    )

    constructor(
        presenterSelector: PresenterSelector,
        config: AsyncDifferConfig<T>
    ) : super(
        presenterSelector
    ) {
        differ = AsyncPagedListDiffer(this, config)
        differ.addPagedListListener(listener)
    }

    /**
     * @link
     * ListUpdateCallback#onInserted
     */
    override fun onInserted(position: Int, count: Int) {
        notifyItemRangeInserted(position, count)
    }

    /**
     * @link
     * ListUpdateCallback#onRemoved
     */
    override fun onRemoved(position: Int, count: Int) {
        notifyItemRangeRemoved(position, count)
    }

    /**
     * @link
     * ListUpdateCallback#onMoved
     */
    override fun onMoved(fromPosition: Int, toPosition: Int) {
        notifyItemMoved(fromPosition, toPosition)
    }

    /**
     * @link
     * ListUpdateCallback#onChanged
     */
    override fun onChanged(position: Int, count: Int, payload: Any?) {
        notifyItemRangeChanged(position, count, payload)
    }

    /**
     * Set the new list to be displayed.
     *
     *
     * If a list is already being displayed, a diff will be computed on a background thread, which
     * will dispatch Adapter.notifyItem events on the main thread.
     *
     * @param pagedList The new list to be displayed.
     */
    fun submitList(pagedList: PagedList<T>?) {
        differ.submitList(pagedList)
    }

    /**
     * Set the new list to be displayed.
     *
     *
     * If a list is already being displayed, a diff will be computed on a background thread, which
     * will dispatch Adapter.notifyItem events on the main thread.
     *
     *
     * The commit callback can be used to know when the PagedList is committed, but note that it
     * may not be executed. If PagedList B is submitted immediately after PagedList A, and is
     * committed directly, the callback associated with PagedList A will not be run.
     *
     * @param pagedList The new list to be displayed.
     * @param commitCallback Optional runnable that is executed when the PagedList is committed, if
     * it is committed.
     */
    fun submitList(pagedList: PagedList<T>?, commitCallback: Runnable?) {
        differ.submitList(pagedList, commitCallback)
    }


    /**
     * @link
     * ArrayObjectAdapter#get
     */
    override fun get(index: Int): T? {
        return differ.getItem(index)
    }

    /**
     * @link
     * ArrayObjectAdapter#size
     */
    override fun size(): Int {
        return differ.itemCount
    }

    /**
     * Returns the PagedList currently being displayed by the Adapter.
     * <p>
     * This is not necessarily the most recent list passed to
     * @link
     * #submitList(PagedList)
     * because a diff is computed asynchronously between the new list and the current list before
     * updating the currentList value. May be null if no PagedList is being presented.
     *
     * @return The list currently being displayed.
     *
     * @link
     * #onCurrentListChanged(PagedList, PagedList)
     */
    fun getCurrentList(): PagedList<T>? = differ.currentList

    @Deprecated("")
    fun onCurrentListChanged(currentList: PagedList<T>?) {
    }

    @Suppress("MemberVisibilityCanBePrivate", "UNUSED_PARAMETER")
    fun onCurrentListChanged(previousList: PagedList<T>?, currentList: PagedList<T>?) {
    }
}
于 2020-05-01T18:34:11.147 回答