بارگیری و نمایش داده های صفحه بندی شده

کتابخانه Paging قابلیت‌های قدرتمندی را برای بارگذاری و نمایش داده‌های صفحه‌بندی‌شده از یک مجموعه داده بزرگتر فراهم می‌کند. این راهنما نحوه استفاده از کتابخانه Paging را برای راه‌اندازی جریانی از داده‌های صفحه‌بندی‌شده از یک منبع داده شبکه و نمایش آن در یک لیست تنبل نشان می‌دهد.

تعریف منبع داده

اولین قدم، تعریف یک پیاده‌سازی PagingSource برای شناسایی منبع داده است. کلاس PagingSource API شامل متد load است که شما آن را برای نشان دادن نحوه بازیابی داده‌های صفحه‌بندی شده از منبع داده مربوطه، بازنویسی می‌کنید.

برای استفاده از کوروتین‌های کاتلین جهت بارگذاری ناهمگام، مستقیماً از کلاس PagingSource استفاده کنید.

انتخاب انواع کلید و مقدار

PagingSource<Key, Value> دو پارامتر نوع دارد: Key و Value . کلید، شناسه‌ای را که برای بارگذاری داده‌ها استفاده می‌شود تعریف می‌کند و مقدار، نوع خود داده‌ها است. برای مثال، اگر صفحات اشیاء User را با ارسال شماره صفحات Int به Retrofit از شبکه بارگذاری می‌کنید، Int به عنوان نوع Key و User به عنوان نوع Value انتخاب کنید.

تعریف منبع صفحه‌بندی

مثال زیر یک PagingSource پیاده‌سازی می‌کند که صفحات آیتم‌ها را بر اساس شماره صفحه بارگذاری می‌کند. نوع Key برابر با Int و نوع Value User است.

class ExamplePagingSource(
    val backend: ExampleBackendService,
    val query: String
) : PagingSource<Int, User>() {
  override suspend fun load(
    params: LoadParams<Int>
  ): LoadResult<Int, User> {

    init {
        // the data source is expected to be immutable
        // invalidate PagingSource if data source
        // has updated
        backEnd.addDatabaseOnChangedListener {
            invalidate()
        }
    }

    try {
      // Start refresh at page 1 if undefined.
      val nextPageNumber = params.key ?: 1
      val response = backend.searchUsers(query, nextPageNumber)
      return LoadResult.Page(
        data = response.users,
        prevKey = null, // Only paging forward.
        nextKey = nextPageNumber + 1
      )
    } catch (e: Exception) {
      // Handle errors in this block and return LoadResult.Error for
      // expected errors (such as a network failure).
    }
  }

  override fun getRefreshKey(state: PagingState<Int, User>): Int? {
    // Try to find the page key of the closest page to anchorPosition from
    // either the prevKey or the nextKey; you need to handle nullability
    // here.
    //  * prevKey == null -> anchorPage is the first page.
    //  * nextKey == null -> anchorPage is the last page.
    //  * both prevKey and nextKey are null -> anchorPage is the
    //    initial page, so return null.
    return state.anchorPosition?.let { anchorPosition ->
      val anchorPage = state.closestPageToPosition(anchorPosition)
      anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
    }
  }
}

یک پیاده‌سازی معمول PagingSource پارامترهای ارائه شده در سازنده‌اش را به متد load ارسال می‌کند تا داده‌های مناسب برای یک پرس‌وجو را بارگذاری کند. در مثال بالا، این پارامترها عبارتند از:

  • backend : نمونه‌ای از سرویس backend که داده‌ها را فراهم می‌کند
  • query : عبارت جستجویی که قرار است به سرویس مشخص شده توسط backend ارسال شود.

شیء LoadParams حاوی اطلاعاتی در مورد عملیات بارگذاری است که باید انجام شود. این شامل کلیدی که باید بارگذاری شود و تعداد آیتم‌هایی که باید بارگذاری شوند، می‌شود.

شیء LoadResult حاوی نتیجه عملیات بارگذاری است. LoadResult یک کلاس مهر و موم شده است که بسته به اینکه آیا فراخوانی load موفقیت آمیز بوده است یا خیر، یکی از سه شکل زیر را به خود می‌گیرد:

  • اگر بارگذاری موفقیت‌آمیز باشد، یک شیء LoadResult.Page برمی‌گرداند.
  • اگر بارگذاری موفقیت‌آمیز نبود، یک شیء LoadResult.Error برمی‌گرداند.
  • اگر PagingSource دیگر معتبر نیست و باید با یک نمونه جدید جایگزین شود (برای مثال، به دلیل تغییر داده‌های اساسی)، یک شیء LoadResult.Invalid برگردانید.

شکل زیر نشان می‌دهد که چگونه تابع load در این مثال، کلید هر بارگذاری را دریافت کرده و کلید بارگذاری بعدی را ارائه می‌دهد.

در هر فراخوانی بارگذاری، ExamplePagingSource کلید فعلی را دریافت کرده و کلید بعدی را برای بارگذاری برمی‌گرداند.
شکل ۱. نموداری که نحوه‌ی استفاده و به‌روزرسانی کلید load را نشان می‌دهد.

پیاده‌سازی PagingSource همچنین باید یک متد getRefreshKey پیاده‌سازی کند که یک شیء PagingState را به عنوان پارامتر می‌گیرد. این متد، کلیدی را برمی‌گرداند تا در صورت به‌روزرسانی یا نامعتبر شدن داده‌ها پس از بارگذاری اولیه، به متد load ارسال شود. کتابخانه Paging این متد را به طور خودکار در به‌روزرسانی‌های بعدی داده‌ها فراخوانی می‌کند.

مدیریت خطاها

درخواست‌های بارگذاری داده‌ها می‌توانند به دلایل مختلفی با شکست مواجه شوند، به خصوص هنگام بارگذاری از طریق شبکه. خطاهای رخ داده در هنگام بارگذاری را با بازگرداندن شیء LoadResult.Error از متد load گزارش دهید.

برای مثال، می‌توانید با اضافه کردن کد زیر به متد load ، خطاهای بارگذاری در ExamplePagingSource از مثال قبلی را دریافت و گزارش کنید:

catch (e: IOException) {
  // IOException for network failures.
  return LoadResult.Error(e)
} catch (e: HttpException) {
  // HttpException for any non-2xx HTTP status codes.
  return LoadResult.Error(e)
}

برای اطلاعات بیشتر در مورد مدیریت خطاهای Retrofit، به نمونه‌های موجود در مرجع PagingSource API مراجعه کنید.

PagingSource اشیاء LoadResult.Error را جمع‌آوری و به رابط کاربری ارائه می‌دهد تا بتوانید روی آنها کاری انجام دهید. برای اطلاعات بیشتر در مورد نمایش وضعیت بارگذاری در رابط کاربری، به مدیریت و ارائه وضعیت‌های بارگذاری مراجعه کنید.

تنظیم جریانی از PagingData

در مرحله بعد، به یک جریان از داده‌های صفحه‌بندی شده از پیاده‌سازی PagingSource نیاز دارید. جریان داده را در ViewModel خود تنظیم کنید. کلاس Pager متدهایی را ارائه می‌دهد که یک جریان واکنشی از اشیاء PagingData را از PagingSource در معرض نمایش قرار می‌دهند. کتابخانه Paging جریان داده‌ها را به عنوان یک Flow در معرض نمایش قرار می‌دهد.

وقتی یک نمونه Pager برای راه‌اندازی جریان واکنشی خود ایجاد می‌کنید، باید یک شیء پیکربندی PagingConfig و یک تابع که به Pager می‌گوید چگونه یک نمونه از پیاده‌سازی PagingSource شما را دریافت کند، همانطور که در مثال زیر نشان داده شده است، به آن نمونه ارائه دهید.

class UserViewModel(
    private val backend: ExampleBackendService,
    private val query: String
) : ViewModel() {

    val userPagingFlow: Flow<PagingData<User>> = Pager(
        // Configure how data is loaded by passing additional properties to
        // PagingConfig, such as pageSize and enabling or disabling placeholders.
        config = PagingConfig(
            pageSize = 20,
            enablePlaceholders = true
        ),
        pagingSourceFactory = {
            ExamplePagingSource(backend, query)
        }
    )
    .flow
    .cachedIn(viewModelScope)
}

The cachedIn operator makes the data stream shareable and caches the loaded data with the provided CoroutineScope . Without cachedIn , the PagingData cannot be recollected on. This example uses the viewModelScope provided by the lifecycle lifecycle-viewmodel-ktx artifact.

شیء Pager متد load را از شیء PagingSource فراخوانی می‌کند، شیء LoadParams را در اختیار آن قرار می‌دهد و در عوض شیء LoadResult دریافت می‌کند.

داده‌ها را در رابط کاربری خود جمع‌آوری و نمایش دهید

برای اتصال جریان صفحه‌بندی شده به رابط کاربری، جریان را از ViewModel خود دریافت کرده و آن را به لیست composable خود منتقل کنید.

@Composable
fun UserScreen(viewModel: UserViewModel = viewModel()) {
    val userFlow = viewModel.userPagingFlow
    UserList(flow = userFlow)
}

از collectAsLazyPagingItems برای تبدیل جریان PagingData به LazyPagingItems استفاده کنید. سپس، از API items درون یک LazyColumn برای طرح‌بندی هر آیتم استفاده کنید.

مطمئن شوید که برای هر آیتم با استفاده از itemKey یک شناسه منحصر به فرد و پایدار ارائه می‌دهید. مثال زیر it.id (با ارجاع به ویژگی User.id ) استفاده می‌کند زیرا برای نمونه User در طول به‌روزرسانی‌های داده‌ها پایدار می‌ماند.

@Composable
fun UserList(flow: Flow<PagingData<User>>) {
    val lazyPagingItems = flow.collectAsLazyPagingItems()
    LazyColumn {
        items(
            lazyPagingItems.itemCount,
            key = lazyPagingItems.itemKey { it.id }
        ) { index ->
            val user = lazyPagingItems[index]
            if (user != null) {
                UserRow(user)
            } else {
                UserPlaceholder()
            }
        }
    }
}

کتابخانه Paging در هنگام بارگذاری صفحه null برای placeholder ها استفاده می‌کند، بنابراین اگر placeholder ها را فعال کرده‌اید، باید مقادیر null را در بلوک محتوا مدیریت کنید.

اکنون لیست، داده‌های صفحه‌بندی‌شده را نمایش می‌دهد و کتابخانه Paging صفحات اضافی را با اسکرول کردن کاربر بارگذاری می‌کند.

منابع اضافی

برای کسب اطلاعات بیشتر در مورد کتابخانه Paging، به منابع اضافی زیر مراجعه کنید:

مستندات

محتوا را مشاهده می‌کند

{% کلمه به کلمه %} {% فعل کمکی %} {% کلمه به کلمه %} {% فعل کمکی %}