جمع البيانات المقسّمة إلى صفحات

يعتمد هذا الدليل على نظرة عامة على مكتبة الصفحات، حيث يناقش كيفية تخصيص حل تحميل بيانات تطبيقك لتلبية احتياجات بنية تطبيقك.

إنشاء قائمة يمكن قياسها

عادةً ما يلاحظ رمز واجهة المستخدم كائن LiveData<PagedList> (أو كائن RxJava2، Flowable<PagedList> أو Observable<PagedList>)، وهو موجود في ViewModel لتطبيقك. ويشكّل هذا العنصر الذي يمكن ملاحظته رابطًا بين العرض التقديمي ومحتويات بيانات قائمة تطبيقك.

لإنشاء أحد هذه الكائنات PagedList القابلة للملاحظة، أرسِل مثيل DataSource.Factory إلى كائن LivePagedListBuilder أو RxPagedListBuilder. يُحمِّل كائن DataSource صفحات لـ PagedList واحد. تنشئ فئة المصنع نُسخًا جديدة من PagedList استجابةً لتحديثات المحتوى، مثل عمليات إلغاء صلاحية جدول قاعدة البيانات وعمليات إعادة تحميل الشبكة. ويمكن أن توفّر لك مكتبة البقاء في الغرفة عناصر DataSource.Factory، أو يمكنك إنشاء كائنات خاصة بك.

يوضِّح مقتطف الرمز التالي كيفية إنشاء مثيل جديد من LiveData<PagedList> في فئة ViewModel لتطبيقك باستخدام إمكانات إنشاء DataSource.Factory في الغرفة:

كونسيرتDao

Kotlin

@Dao
interface ConcertDao {
    // The Int type parameter tells Room to use a PositionalDataSource
    // object, with position-based loading under the hood.
    @Query("SELECT * FROM concerts ORDER BY date DESC")
    fun concertsByDate(): DataSource.Factory<Int, Concert>
}

Java

@Dao
public interface ConcertDao {
    // The Integer type parameter tells Room to use a PositionalDataSource
    // object, with position-based loading under the hood.
    @Query("SELECT * FROM concerts ORDER BY date DESC")
    DataSource.Factory<Integer, Concert> concertsByDate();
}

نموذج ConcertViewModel

Kotlin

// The Int type argument corresponds to a PositionalDataSource object.
val myConcertDataSource : DataSource.Factory<Int, Concert> =
       concertDao.concertsByDate()

val concertList = myConcertDataSource.toLiveData(pageSize = 50)

Java

// The Integer type argument corresponds to a PositionalDataSource object.
DataSource.Factory<Integer, Concert> myConcertDataSource =
       concertDao.concertsByDate();

LiveData<PagedList<Concert>> concertList =
        LivePagedListBuilder(myConcertDataSource, /* page size */ 50).build();

تحديد إعدادات نقل الصفحات الخاصة بك

لضبط LiveData<PagedList> بشكلٍ أكبر للحالات المتقدّمة، يمكنك أيضًا تحديد إعدادات نقل الصفحات الخاصة بك. وعلى وجه الخصوص، يمكنك تعريف السمات التالية:

  • حجم الصفحة: عدد العناصر في كل صفحة
  • المسافة الجلب المسبق: بالنظر إلى آخر عنصر مرئي في واجهة مستخدم التطبيق، عدد العناصر التي يجب أن تحاول "مكتبة الترحيل" جلبها مقدّمًا بعد هذا العنصر الأخير. يجب أن تكون هذه القيمة أكبر بضع مرات من حجم الصفحة.
  • تواجد العناصر النائبة: تحدد ما إذا كانت واجهة المستخدم تعرض عناصر نائبة لعناصر القائمة التي لم ينتهِ تحميلها بعد. لإجراء مناقشة حول مزايا وعيوب استخدام العناصر النائبة، تعرَّف على كيفية توفير العناصر النائبة في واجهة المستخدم.

إذا كنت ترغب في المزيد من التحكم في وقت تحميل "مكتبة تسجيل الصفحات" لقائمة من قاعدة بيانات تطبيقك، مرِّر كائن Executor مخصَّصًا إلى LivePagedListBuilder، كما هو موضّح في مقتطف الرمز التالي:

نموذج ConcertViewModel

Kotlin

val myPagingConfig = Config(
        pageSize = 50,
        prefetchDistance = 150,
        enablePlaceholders = true
)

// The Int type argument corresponds to a PositionalDataSource object.
val myConcertDataSource : DataSource.Factory<Int, Concert> =
        concertDao.concertsByDate()

val concertList = myConcertDataSource.toLiveData(
        pagingConfig = myPagingConfig,
        fetchExecutor = myExecutor
)

Java

PagedList.Config myPagingConfig = new PagedList.Config.Builder()
        .setPageSize(50)
        .setPrefetchDistance(150)
        .setEnablePlaceholders(true)
        .build();

// The Integer type argument corresponds to a PositionalDataSource object.
DataSource.Factory<Integer, Concert> myConcertDataSource =
        concertDao.concertsByDate();

LiveData<PagedList<Concert>> concertList =
        new LivePagedListBuilder<>(myConcertDataSource, myPagingConfig)
            .setFetchExecutor(myExecutor)
            .build();

اختيار نوع مصدر البيانات الصحيح

من المهم الاتصال بمصدر البيانات الذي يعالج هيكل بيانات المصدر بشكل أفضل:

  • استخدِم PageKeyedDataSource إذا قمت بتحميل الصفحات التي تضمّنت مفاتيح تتضمّن التالي/السابق. على سبيل المثال، إذا كنت تجلب مشاركات على وسائل التواصل الاجتماعي من الشبكة، قد تحتاج إلى تمرير رمز مميّز nextPage من حِمل واحد إلى عملية تحميل لاحقة.
  • استخدِم ItemKeyedDataSource إذا كنت بحاجة إلى استخدام بيانات من العنصر N لجلب العنصر N+1. على سبيل المثال، إذا كنت تجلب تعليقات متسلسلة لتطبيق مناقشة، فقد تحتاج إلى تمرير معرّف التعليق الأخير للحصول على محتويات التعليق التالي.
  • استخدِم PositionalDataSource إذا كنت بحاجة إلى استرجاع صفحات البيانات من أي موقع جغرافي تختاره في متجر البيانات. تتيح هذه الفئة طلب مجموعة من عناصر البيانات بدءًا من أي موقع جغرافي تختاره. على سبيل المثال، قد يعرض الطلب 50 عنصر بيانات يبدأ بالموقع 1500.

إرسال إشعار عندما تكون البيانات غير صالحة

عند استخدام "مكتبة الترحيل"، يرجع الأمر إلى طبقة البيانات في إشعار الطبقات الأخرى من تطبيقك عندما يصبح جدول أو صف قديمين. ولإجراء ذلك، اتصل بالرقم invalidate() من الصف DataSource الذي اخترته لتطبيقك.

إنشاء مصادر البيانات الخاصة بك

إذا كنت تستخدم حلاً مخصّصًا للبيانات المحلية، أو إذا حمَّلت البيانات مباشرةً من شبكة، يمكنك تنفيذ إحدى الفئات الفرعية DataSource. يعرض مقتطف الرمز التالي مصدر بيانات تم تسجيله قبل وقت بدء حفلة موسيقية:

Kotlin

class ConcertTimeDataSource() :
        ItemKeyedDataSource<Date, Concert>() {
    override fun getKey(item: Concert) = item.startTime

    override fun loadInitial(
            params: LoadInitialParams<Date>,
            callback: LoadInitialCallback<Concert>) {
        val items = fetchItems(params.requestedInitialKey,
                params.requestedLoadSize)
        callback.onResult(items)
    }

    override fun loadAfter(
            params: LoadParams<Date>,
            callback: LoadCallback<Concert>) {
        val items = fetchItemsAfter(
            date = params.key,
            limit = params.requestedLoadSize)
        callback.onResult(items)
    }
}

Java

public class ConcertTimeDataSource
        extends ItemKeyedDataSource<Date, Concert> {
    @NonNull
    @Override
    public Date getKey(@NonNull Concert item) {
        return item.getStartTime();
    }

    @Override
    public void loadInitial(@NonNull LoadInitialParams<Date> params,
            @NonNull LoadInitialCallback<Concert> callback) {
        List<Concert> items =
            fetchItems(params.key, params.requestedLoadSize);
        callback.onResult(items);
    }

    @Override
    public void loadAfter(@NonNull LoadParams<Date> params,
            @NonNull LoadCallback<Concert> callback) {
        List<Concert> items =
            fetchItemsAfter(params.key, params.requestedLoadSize);
        callback.onResult(items);
    }

ويمكنك بعد ذلك تحميل هذه البيانات المخصّصة إلى عناصر PagedList من خلال إنشاء فئة فرعية ملموسة من DataSource.Factory. يوضّح مقتطف الرمز التالي كيفية إنشاء مثيلات جديدة لمصدر البيانات المخصّصة المحدّد في مقتطف الرمز السابق:

Kotlin

class ConcertTimeDataSourceFactory :
        DataSource.Factory<Date, Concert>() {
    val sourceLiveData = MutableLiveData<ConcertTimeDataSource>()
    var latestSource: ConcertDataSource?
    override fun create(): DataSource<Date, Concert> {
        latestSource = ConcertTimeDataSource()
        sourceLiveData.postValue(latestSource)
        return latestSource
    }
}

Java

public class ConcertTimeDataSourceFactory
        extends DataSource.Factory<Date, Concert> {
    private MutableLiveData<ConcertTimeDataSource> sourceLiveData =
            new MutableLiveData<>();

    private ConcertDataSource latestSource;

    @Override
    public DataSource<Date, Concert> create() {
        latestSource = new ConcertTimeDataSource();
        sourceLiveData.postValue(latestSource);
        return latestSource;
    }
}

التفكير في طريقة عمل تحديثات المحتوى

أثناء إنشاء عناصر PagedList يمكن ملاحظتها، عليك مراعاة آلية عمل تعديلات المحتوى. في حال تحميل البيانات مباشرةً من قاعدة بيانات الغرفة، يتم إرسال تحديثات إلى واجهة مستخدم التطبيق تلقائيًا.

عند استخدام واجهة برمجة تطبيقات للشبكة المقسّمة على صفحات، يكون عادةً تفاعل المستخدم، مثل "التمرير سريعًا لإعادة التحميل"، بمثابة إشارة لإلغاء صلاحية DataSource التي استخدمتها مؤخرًا. يمكنك بعد ذلك طلب مثيل جديد لمصدر البيانات هذا. يوضح مقتطف الرمز التالي هذا السلوك:

Kotlin

class ConcertActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        // ...
        concertTimeViewModel.refreshState.observe(this, Observer {
            // Shows one possible way of triggering a refresh operation.
            swipeRefreshLayout.isRefreshing =
                    it == MyNetworkState.LOADING
        })
        swipeRefreshLayout.setOnRefreshListener {
            concertTimeViewModel.invalidateDataSource()
        }
    }
}

class ConcertTimeViewModel(firstConcertStartTime: Date) : ViewModel() {
    val dataSourceFactory = ConcertTimeDataSourceFactory(firstConcertStartTime)
    val concertList: LiveData<PagedList<Concert>> =
            dataSourceFactory.toLiveData(
                pageSize = 50,
                fetchExecutor = myExecutor
            )

    fun invalidateDataSource() =
            dataSourceFactory.sourceLiveData.value?.invalidate()
}

Java

public class ConcertActivity extends AppCompatActivity {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        // ...
        viewModel.getRefreshState()
                .observe(this, new Observer<NetworkState>() {
            // Shows one possible way of triggering a refresh operation.
            @Override
            public void onChanged(@Nullable MyNetworkState networkState) {
                swipeRefreshLayout.isRefreshing =
                        networkState == MyNetworkState.LOADING;
            }
        };

        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshListener() {
            @Override
            public void onRefresh() {
                viewModel.invalidateDataSource();
            }
        });
    }
}

public class ConcertTimeViewModel extends ViewModel {
    private LiveData<PagedList<Concert>> concertList;
    private DataSource<Date, Concert> mostRecentDataSource;

    public ConcertTimeViewModel(Date firstConcertStartTime) {
        ConcertTimeDataSourceFactory dataSourceFactory =
                new ConcertTimeDataSourceFactory(firstConcertStartTime);
        mostRecentDataSource = dataSourceFactory.create();
        concertList = new LivePagedListBuilder<>(dataSourceFactory, 50)
                .setFetchExecutor(myExecutor)
                .build();
    }

    public void invalidateDataSource() {
        mostRecentDataSource.invalidate();
    }
}

توفير تعيين البيانات

تتيح "مكتبة ترقيم الصفحات" إمكانية إجراء عمليات تحويل مستندة إلى العناصر وإلى الصفحة للعناصر التي يتم تحميلها من خلال DataSource.

في مقتطف الرمز التالي، يتم تعيين مجموعة من اسم الحفلة الموسيقية وتاريخها في سلسلة واحدة تحتوي على الاسم والتاريخ:

Kotlin

class ConcertViewModel : ViewModel() {
    val concertDescriptions : LiveData<PagedList<String>>
        init {
            val concerts = database.allConcertsFactory()
                    .map { "${it.name} - ${it.date}" }
                    .toLiveData(pageSize = 50)
        }
}

Java

public class ConcertViewModel extends ViewModel {
    private LiveData<PagedList<String>> concertDescriptions;

    public ConcertViewModel(MyDatabase database) {
        DataSource.Factory<Integer, Concert> factory =
                database.allConcertsFactory().map(concert ->
                    concert.getName() + "-" + concert.getDate());
        concertDescriptions = new LivePagedListBuilder<>(
            factory, /* page size */ 50).build();
    }
}

يمكن أن يكون هذا مفيدًا إذا كنت تريد لف العناصر أو تحويلها أو تحضيرها بعد تحميلها. ولأنّ هذا العمل يتم على الجهاز تنفيذ الجلب، يمكنك القيام بأعمال قد تكون مكلّفة، مثل القراءة من القرص أو الاستعلام عن قاعدة بيانات منفصلة.

تقديم ملاحظات

يُرجى مشاركة ملاحظاتك وآرائك معنا من خلال الموارد التالية:

أداة تتبّع المشاكل
يمكنك الإبلاغ عن المشاكل حتى نتمكّن من إصلاح الأخطاء.

مراجع إضافية

لمعرفة المزيد حول مكتبة الترحيل، يمكنك الرجوع إلى الموارد التالية.

عيّنات

الدروس التطبيقية حول الترميز

الفيديوهات الطويلة