如何使用Paging Library在Leanback VerticalGridSupportFragment中实现分页?

14
我们正在尝试使用Architecture Components Paging Library在Leanback VerticalGridSupportFragment中实现分页。由于Leanback本身没有任何与Paging Library的兼容性,所以我们扩展了它的ObjectAdapter类,并成功实现了追加和清除操作。但是修改操作却令我们很难实现。在内容修改操作期间,Paging Library的PagedList类使用AsyncPagedListDiffer计算差异,它内部使用PagedStorageDiffHelper,而它是一个包私有类,它内部使用PagedList的包私有PagedStorage字段来访问实际底层数据。因此,由于可见性限制,我们无法实现与Paging Library内部相同的逻辑。我们正在寻找一种干净而巧妙的方法,使Leanback能够与Paging一起工作,而不需要提取和修改任何两者的内部。这是我们支持追加和清除数据的ObjectAdapter的实现,但不支持内容修改。请问是否有人曾经通过Paging Library实现了在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"))
        }
    }
}

1
你好,你找到解决问题的方法了吗?我也在寻找一种干净的方式来实现Leanback上的分页。谢谢! - AresProductions
现在有一个处于alpha状态的leanback-paging模块,它实现了paging3所需的必要钩子:https://maven.google.com/web/index.html#androidx.leanback:leanback-paging:1.1.0-alpha04 - dlam
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>?) {
    }
}

你好,能否请您分享一下完整的示例,展示PagedArrayObjectAdapter如何与leanback一起使用? - Piyush
以与PagedListAdapter完全相同的方式,创建从分页获取的数据并调用submitList(PagedList) - Swapnil Bhoite

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接