إدارة حالات التحميل وتقديمها

تتتبّع مكتبة Paging حالة طلبات التحميل للبيانات المقسَّمة إلى صفحات وتعرضها من خلال فئة LoadState.

يتم توفير إشارة LoadState منفصلة لكل LoadType ونوع مصدر البيانات (إما PagingSource أو RemoteMediator). يقدّم عنصر CombinedLoadStates الذي يوفّره المتتبِّع معلومات عن حالة التحميل من كل هذه الإشارات. يمكنك استخدام هذه المعلومات التفصيلية لعرض مؤشرات التحميل المناسبة للمستخدِمين.

حالات التحميل

تعرض مكتبة Paging حالة التحميل لاستخدامها في واجهة المستخدِم من خلال عنصر LoadState. تتخذ عناصر LoadState أحد الأشكال الثلاثة التالية استنادًا إلى حالة التحميل الحالية:

  • إذا لم تكن هناك عملية تحميل نشطة ولم يحدث أي خطأ، يكون LoadState عنصر LoadState.NotLoading. يتضمّن هذا الفئة الفرعية أيضًا السمة endOfPaginationReached التي تشير إلى ما إذا تم الوصول إلى نهاية الترقيم.
  • إذا كانت هناك عملية تحميل نشطة، يكون LoadState عنصر LoadState.Loading.
  • إذا حدث خطأ، يكون LoadState عنصر LoadState.Error.

يمكنك الوصول إلى هذه الحالات من خلال السمة loadState لغلاف LazyPagingItems. يمكنك استخدام هذه الحالة بطريقتَين: معالجة مدى ظهور المحتوى الرئيسي (مثل مؤشر سريان العمل الخاص بتحديث ملء الشاشة) أو إدراج عناصر التحميل مباشرةً في مصدر بيانات LazyColumn (مثل مؤشر سريان العمل الخاص بالتذييل).

الوصول إلى حالة التحميل باستخدام مستمع

لمراقبة حالة التحميل في واجهة المستخدِم، استخدِم السمة loadStateproperty التي يوفّرها غلاف LazyPagingItems. تعرض هذه السمة عنصر CombinedLoadStates يتيح لك التفاعل مع سلوك التحميل لأحداث التحديث أو الإلحاق أو الإضافة.

في المثال التالي، تعرض واجهة المستخدِم مؤشر سريان عمل خاصًا بالتحميل أو رسالة خطأ استنادًا إلى الحالة الحالية لتحميل التحديث (الأولي):

@Composable
fun UserListScreen(viewModel: UserViewModel) {
  val pagingItems = viewModel.flow.collectAsLazyPagingItems()

  Box(modifier = Modifier.fillMaxSize()) {
    // Show the list content
    LazyColumn {
      items(pagingItems.itemCount) { index ->
        UserItem(pagingItems[index])
      }
    }

    // Handle the loading state
    when (val state = pagingItems.loadState.refresh) {
      is LoadState.Loading -> {
        CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
      }
      is LoadState.Error -> {
        ErrorButton(
          message = state.error.message ?: "Unknown error",
          onClick = { pagingItems.retry() },
          modifier = Modifier.align(Alignment.Center)
        )
      }
      else -> {} // No separate view needed for success/not loading
    }
  }
}

لمزيد من المعلومات عن LazyPagingItems، اطّلِع على مجموعات البيانات الكبيرة (الترقيم).

لعرض مؤشرات التحميل في بداية القائمة أو نهايتها (تعمل كرؤوس أو تذييلات)، أضِف كتل عناصر مخصّصة لحالات التحميل هذه تحديدًا ضمن نطاق LazyColumn.

يمكنك مراقبة حالة الإضافة للرأس وحالة الإلحاق للتذييل باستخدام الـ CombinedLoadStates.

في المثال التالي، تعرض القائمة شريط تقدّم أو زر إعادة المحاولة في أسفل القائمة عند جلب المزيد من البيانات:

@Composable
fun UserList(viewModel: UserViewModel) {
  val pagingItems = viewModel.pager.flow.collectAsLazyPagingItems()

  LazyColumn {
    // 1. Header (Prepend state)
    // Useful if you support bidirectional paging or jumping to the middle
    item {
      val prependState = pagingItems.loadState.prepend
      if (prependState is LoadState.Loading) {
        LoadingItem()
      } else if (prependState is LoadState.Error) {
        ErrorItem(
          message = prependState.error.message ?: "Error",
          onClick = { pagingItems.retry() }
        )
      }
    }

    // 2. Main Data
    items(pagingItems.itemCount) { index ->
      UserItem(pagingItems[index])
    }

    // 3. Footer (Append state)
    // Shows when the user scrolls to the bottom and more data is loading
    item {
      val appendState = pagingItems.loadState.append
      if (appendState is LoadState.Loading) {
        LoadingItem()
      } else if (appendState is LoadState.Error) {
        ErrorItem(
          message = appendState.error.message ?: "Error",
          onClick = { pagingItems.retry() }
        )
      }
    }
  }
}

@Composable
fun LoadingItem() {
  Box(modifier = Modifier.fillMaxWidth().padding(16.dp), contentAlignment = Alignment.Center) {
    CircularProgressIndicator()
  }
}

@Composable
fun ErrorItem(message: String, onClick: () -> Unit) {
  Column(
    modifier = Modifier.fillMaxWidth().padding(16.dp),
    horizontalAlignment = Alignment.CenterHorizontally
  ) {
    Text(text = message, color = Color.Red)
    Button(onClick = onClick) { Text("Retry") }
  }
}

الوصول إلى معلومات إضافية عن حالة التحميل

كما هو موضّح في الأمثلة السابقة، من الملائم استدعاء pagingItems.loadState.refresh. ومع ذلك، يحجب هذا الإجراء الفرق بين التحميل من قاعدة البيانات المحلية (PagingSource) والشبكة (RemoteMediator). ويمكن أن يؤدي ذلك إلى أن تعرض واجهة المستخدِم مؤشر سريان عمل خاصًا بالتحميل لفترة وجيزة حتى عندما تكون البيانات المخزّنة مؤقتًا متاحة على الفور.

للحصول على تحكّم دقيق، مثل عرض مؤشر سريان عمل خاص بالتحميل فقط عندما تكون قاعدة البيانات المحلية فارغة ويكون مزامنة الشبكة نشطة، يمكنك الوصول إلى سمتَي source وmediator مباشرةً ضمن العنصر القابل للإنشاء.

val loadState = pagingItems.loadState

val isSyncing = loadState.mediator?.refresh is LoadState.Loading

val isLocalEmpty = loadState.source.refresh is LoadState.NotLoading &&
                   pagingItems.itemSnapshotList.items.isEmpty()

if (isSyncing && isLocalEmpty) {
    FullScreenLoading()
} else {
    UserList(pagingItems)

    if (isSyncing) {
        TopOverlaySpinner()
    }
}

التفاعل مع تغييرات حالة التحميل

قد تحتاج إلى تشغيل آثار جانبية لمرة واحدة استنادًا إلى تغييرات حالة التحميل، مثل الانتقال إلى أعلى القائمة أو عرض Snackbar عند اكتمال التحديث.

استخدِم snapshotFlow داخل LaunchedEffect لمراقبة تغييرات الحالة كمصدر بيانات. يتيح لك ذلك تطبيق عوامل تشغيل Flow العادية، مثل filter و distinctUntilChanged لعزل أحداث معيّنة.

val listState = rememberLazyListState()

LaunchedEffect(pagingItems) {
  // 1. Convert the state to a Flow
  snapshotFlow { pagingItems.loadState.refresh }
    // 2. Filter for the specific event (Refresh completed successfully)
    .distinctUntilChanged()
    .filter { it is LoadState.NotLoading }
    .collect {
      // 3. Trigger the side effect
      listState.animateScrollToItem(0)
    }
}

مراجع إضافية

لمزيد من المعلومات عن مكتبة Paging وحالات التحميل، يُرجى الاطّلاع على المراجع التالية.

الوثائق

Content Views