Частичная нумерация страниц - PullRequest
0 голосов
/ 08 января 2020

Я пытаюсь реализовать пейджинговую логику c с частичной загрузкой данных на одну страницу. У меня есть страница с максимальным значением page_size 100, но мне не нужны все 100 значений для одного запроса, мне нужно запросить 30 записей в одном запросе, 30 в секунду, 30 в третьем 10 (из-за макс. 100) в четвертом запрос и после этого я меняю номер страницы. Вот как я написал это сейчас: Класс источника данных

class NewsDataSource(
    private val compositeDisposable: CompositeDisposable,
    private val newsRequests: NewsRequests
) : PageKeyedDataSource<PageNewsKey, News.Data?>() {

    private val pRequestNewsStatuses = MutableLiveData<LiveDataStatuses>(LiveDataStatuses.IDLE)
    val requestNewsStatuses: LiveData<LiveDataStatuses> = pRequestNewsStatuses

    override fun loadInitial(
        params: LoadInitialParams<PageNewsKey>,
        callback: LoadInitialCallback<PageNewsKey, News.Data?>
    ) {
        val pageNewsKey = PageNewsKey()
        pRequestNewsStatuses.postValue(LiveDataStatuses.WAITING)
        compositeDisposable.add(
            newsRequests.getNews(pageNewsKey.newsCount, pageNewsKey.page)
                .subscribeOn(Schedulers.io())
                .map {
                    it.data ?: emptyList()
                }
                .subscribe({
                    callback.onResult(it, null, pageNewsKey.nextPageKey)
                }, {

                })
        )
    }

    override fun loadBefore(
        params: LoadParams<PageNewsKey>,
        callback: LoadCallback<PageNewsKey, News.Data?>
    ) {

    }

    override fun loadAfter(
        params: LoadParams<PageNewsKey>,
        callback: LoadCallback<PageNewsKey, News.Data?>
    ) {
        pRequestNewsStatuses.postValue(LiveDataStatuses.WAITING)
        compositeDisposable.add(
            newsRequests.getNews(params.key.newsCount, params.key.page)
                .subscribeOn(Schedulers.io())
                .map {
                    it.data ?: emptyList()
                }
                .subscribe({
                    callback.onResult(it, params.key.nextPageKey)
                }, {

                })
        )
    }

Класс ключа:

class PageNewsKey {

    var newsCount: Int = NEWS_COUNT_DEFAULT_VALUE
    var page: Int = 1

    val nextPageKey: PageNewsKey
    get() {
        return iterate()
    }

    private fun iterate(): PageNewsKey {
        if (newsCount == NEWS_COUNT_MAX_VALUE) {
            newsCount = NEWS_COUNT_DEFAULT_VALUE
            page = page.inc()
        } else {
            val newNewsCount = newsCount + NEWS_COUNT_DEFAULT_VALUE
            newsCount = if (newNewsCount > NEWS_COUNT_MAX_VALUE) {
                NEWS_COUNT_MAX_VALUE
            } else {
                newNewsCount
            }
        }
        return this
    }

}

Фабрика источника данных:

class NewsDataSourceFactory(
    private val compositeDisposable: CompositeDisposable,
    private val newsRequests: NewsRequests
) : DataSource.Factory<PageNewsKey, News.Data?>() {

    private val pNewsDataSource = MutableLiveData<NewsDataSource?>(null)
    val newsDataSource: LiveData<NewsDataSource?> = pNewsDataSource

    override fun create(): DataSource<PageNewsKey, News.Data?> {
        val newsDataSource = NewsDataSource(compositeDisposable, newsRequests)
        pNewsDataSource.postValue(newsDataSource)
        return newsDataSource
    }

    fun refresh() {
        pNewsDataSource.value?.invalidate()
    }

}

Этот код находится в модели представления класс:

private val newsDataSourceFactory = NewsDataSourceFactory(compositeDisposable, NewsRequests.getNewsRequest())
private val newsDataSourceFactoryConfig = PagedList.Config.Builder()
        .setEnablePlaceholders(true)
        .setPageSize(NEWS_COUNT_DEFAULT_VALUE)
        .build()

val news: LiveData<PagedList<News.Data?>> = LivePagedListBuilder(newsDataSourceFactory, newsDataSourceFactoryConfig).build()

Наблюдатель данных в реальном времени внутри нужного фрагмента:

homeViewModel.news.observe(this, Observer {
            it?.let {
                newsAdapter.submitList(it)
            }
        })

Адаптер:

class NewsAdapter : PagedListAdapter<News.Data, NewsAdapter.ViewHolder>(News.Data.NEWS_DATA_DIFF_CALLBACK) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.news_row, parent, false))
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bingView(getItem(position))
    }

    fun getItemAtPosition(position: Int): News.Data? = getItem(position)

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        private var iv_news_image: ImageView? = null
        private var tv_news_title: TextView? = null
        private var tv_news_subtitle: TextView? = null
        private var clpb_news_image: ContentLoadingProgressBar? = null

        init {
            iv_news_image = itemView.findViewById(R.id.iv_news_image)
            tv_news_title = itemView.findViewById(R.id.tv_news_title)
            tv_news_subtitle = itemView.findViewById(R.id.tv_news_subtitle)
            clpb_news_image = itemView.findViewById(R.id.clpb_news_image)

            clpb_news_image?.hide()
        }

        fun bingView(data: News.Data?) {
            iv_news_image?.let {
                clpb_news_image?.show()
                GlideApp.with(itemView.context)
                    .load(data?.imageUrl)
                    .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
                    .transition(DrawableTransitionOptions.withCrossFade(500))
                    .listener(object : IDoAfterTerminateGlide {
                        override fun doAfterTerminate() {
                            clpb_news_image?.hide()
                        }
                    })
                    .dontAnimate()
                    .into(it)
            }
            tv_news_title?.text = data?.title
            tv_news_subtitle?.text = data?.subtitle
        }

    }

}

И, наконец, класс данных с обратным вызовом diff:

data class News(
    @SerializedName("current_page")
    val currentPage: Int? = null,
    @SerializedName("data")
    val data: List<Data?>? = null,
    @SerializedName("first_page_url")
    val firstPageUrl: String? = null,
    @SerializedName("from")
    val from: Int? = null,
    @SerializedName("last_page")
    val lastPage: Int? = null,
    @SerializedName("last_page_url")
    val lastPageUrl: String? = null,
    @SerializedName("next_page_url")
    val nextPageUrl: String? = null,
    @SerializedName("path")
    val path: String? = null,
    @SerializedName("per_page")
    val perPage: String? = null,
    @SerializedName("prev_page_url")
    val prevPageUrl: String? = null,
    @SerializedName("to")
    val to: Int? = null,
    @SerializedName("total")
    val total: Int? = null
) {
    data class Data(
        @SerializedName("body")
        val body: String? = null,
        @SerializedName("date")
        val date: String? = null,
        @SerializedName("image_url")
        val imageUrl: String? = null,
        @SerializedName("news_id")
        val newsId: String? = null,
        @SerializedName("subtitle")
        val subtitle: String? = null,
        @SerializedName("title")
        val title: String? = null
    ) {
        companion object {
            val NEWS_DATA_DIFF_CALLBACK = object : DiffUtil.ItemCallback<Data>() {
                override fun areItemsTheSame(oldItem: Data, newItem: Data): Boolean {
                    return oldItem.newsId == newItem.newsId
                }

                override fun areContentsTheSame(oldItem: Data, newItem: Data): Boolean {
                    return oldItem.body == newItem.body && oldItem.date == oldItem.date
                            && oldItem.imageUrl == newItem.imageUrl
                            && oldItem.subtitle == newItem.subtitle
                            && oldItem.title == newItem.title
                }
            }
        }
    }
}

json ответ И эта настройка работает, как я ожидал, она делает запросы:

  1. www.example.com?page=1&page_size=30
  2. www.example.com?page=1&page_size=60
  3. www.example.com?page=1&page_size=90
  4. www.example.com?page=1&page_size=10
  5. www.example.com?page=2&page_size=30

Но проблема в том, что обратный вызов элемента diff не делает его работу Я ожидаю, и я получаю дублированные данные. Итак, мой вопрос довольно прост, предоставляет ли android источник данных с ключами страницы такую ​​функциональность?

...