Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang
Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.
Hướng dẫn này dựa trên tổng quan về Thư viện Paging, mô tả cách bạn có thể trình bày danh sách thông tin cho người dùng trong giao diện người dùng của ứng dụng, đặc biệt là khi thông tin này thay đổi.
Kết nối UI (giao diện người dùng) với mô hình chế độ xem
classConcertActivity:AppCompatActivity(){privatevaladapter=ConcertAdapter()// Use the 'by viewModels()' Kotlin property delegate// from the activity-ktx artifactprivatevalviewModel:ConcertViewModelbyviewModels()overridefunonCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState);viewModel.concerts.observe(this,Observer{adapter.submitList(it)})}}
Khi nguồn dữ liệu cung cấp các bản sao mới của PagedList, activity sẽ gửi các đối tượng này đến bộ chuyển đổi. Hoạt động triển khai PagedListAdapter xác định cách tính toán các bản cập nhật và tự động xử lý sự khác biệt giữa phân trang và danh sách. Do đó, ViewHolder chỉ cần ràng buộc với một mục cụ thể đã cung cấp:
Kotlin
classConcertAdapter():PagedListAdapter<Concert,ConcertViewHolder>(DIFF_CALLBACK){overridefunonBindViewHolder(holder:ConcertViewHolder,position:Int){valconcert:Concert? =getItem(position)// Note that "concert" is a placeholder if it's null.holder.bindTo(concert)}companionobject{privatevalDIFF_CALLBACK=...// See Implement the diffing callback section.}}
Java
publicclassConcertAdapterextendsPagedListAdapter<Concert,ConcertViewHolder>{protectedConcertAdapter(){super(DIFF_CALLBACK);}@OverridepublicvoidonBindViewHolder(@NonNullConcertViewHolderholder,intposition){Concertconcert=getItem(position);// Note that "concert" can be null if it's a placeholder.holder.bindTo(concert);}privatestaticDiffUtil.ItemCallback<Concert>DIFF_CALLBACK=...// See Implement the diffing callback section.}
Mẫu sau đây minh hoạ cách triển khai thủ công của areContentsTheSame(), trong đó so sánh các trường đối tượng có liên quan:
Kotlin
privatevalDIFF_CALLBACK=object:DiffUtil.ItemCallback<Concert>(){// The ID property identifies when items are the same.overridefunareItemsTheSame(oldItem:Concert,newItem:Concert)=oldItem.id==newItem.id// If you use the "==" operator, make sure that the object implements// .equals(). Alternatively, write custom data comparison logic here.overridefunareContentsTheSame(oldItem:Concert,newItem:Concert)=oldItem==newItem}
Java
privatestaticDiffUtil.ItemCallback<Concert>DIFF_CALLBACK=newDiffUtil.ItemCallback<Concert>(){@OverridepublicbooleanareItemsTheSame(ConcertoldItem,ConcertnewItem){// The ID property identifies when items are the same.returnoldItem.getId()==newItem.getId();}@OverridepublicbooleanareContentsTheSame(ConcertoldItem,ConcertnewItem){// Don't use the "==" operator here. Either implement and use .equals(),// or write custom data comparison logic here.returnoldItem.equals(newItem);}};
Vì bộ chuyển đổi của bạn bao gồm định nghĩa về việc so sánh các mục, nên bộ chuyển đổi sẽ tự động phát hiện các thay đổi đối với các mục này khi đối tượng PagedList mới được tải. Do đó, bộ chuyển đổi sẽ kích hoạt các hiệu ứng tải mục hiệu quả trong đối tượng RecyclerView.
So sánh bằng loại bộ chuyển đổi khác
Nếu bạn không kế thừa từ PagedListAdapter — chẳng hạn như bạn dùng thư viện cung cấp bộ chuyển đổi riêng — bạn vẫn có thể dùng chức năng khác biệt của bộ chuyển đổi thư viện Paging bằng cách trực tiếp xử lý đối tượng AsyncPagedListDiffer.
Cung cấp phần giữ chỗ trong UI (giao diện người dùng)
Trong trường hợp bạn muốn UI (giao diện người dùng) hiện một danh sách trước khi ứng dụng hoàn tất quá trình tìm nạp dữ liệu, bạn có thể hiện các mục danh sách trong phần giữ chỗ cho người dùng. PagedList xử lý trường hợp này bằng cách trình bày dữ liệu mục danh sách dưới dạng null cho đến khi dữ liệu được tải.
Phần giữ chỗ có những lợi ích sau:
Hỗ trợ thanh cuộn:PagedList cung cấp số lượng mục trong danh sách cho PagedListAdapter. Thông tin này cho phép bộ chuyển đổi vẽ một thanh cuộn để truyền tải kích thước đầy đủ của danh sách. Khi tải các trang mới, thanh cuộn sẽ không nhảy vì danh sách của bạn không thay đổi kích thước.
Không cần vòng quay đang tải: Vì kích thước danh sách đã được xác định, nên bạn không cần cảnh báo người dùng rằng nhiều mục khác đang tải. Phần giữ chỗ sẽ tự truyền tải thông tin đó.
Trước khi thêm khả năng hỗ trợ cho phần giữ chỗ, hãy lưu ý những điều kiện tiên quyết sau đây:
Yêu cầu tập dữ liệu có thể đếm được: Các bản sao của DataSource trong thư viện lưu trữ Room có thể đếm các mục một cách hiệu quả. Tuy nhiên, nếu bạn đang sử dụng giải pháp lưu trữ cục bộ tuỳ chỉnh hoặc cấu trúc dữ liệu chỉ dành cho mạng, thì bạn có thể tốn kém hoặc thậm chí không thể xác định có bao nhiêu mục bao gồm tập dữ liệu của bạn.
Yêu cầu bộ chuyển đổi tính đến các mục chưa tải: Bộ chuyển đổi hoặc cơ chế trình bày mà bạn dùng để chuẩn bị danh sách cho lạm phát cần phải xử lý các mục trong danh sách rỗng. Ví dụ: khi ràng buộc dữ liệu với ViewHolder, bạn cần cung cấp các giá trị mặc định để đại diện cho dữ liệu chưa tải.
Yêu cầu chế độ xem mục có cùng kích thước: Nếu kích thước mục trong danh sách có thể thay đổi theo nội dung của các mục đó, chẳng hạn như nội dung cập nhật trên mạng xã hội, thì việc phân chia giữa các mục sẽ không ổn. Chúng tôi thực sự khuyên bạn nên vô hiệu hoá phần giữ chỗ trong trường hợp này.
Gửi ý kiến phản hồi
Hãy chia sẻ phản hồi và ý kiến của bạn với chúng tôi thông qua các tài nguyên sau:
Nội dung và mã mẫu trên trang này phải tuân thủ các giấy phép như mô tả trong phần Giấy phép nội dung. Java và OpenJDK là nhãn hiệu hoặc nhãn hiệu đã đăng ký của Oracle và/hoặc đơn vị liên kết của Oracle.
Cập nhật lần gần đây nhất: 2025-07-27 UTC.
[[["Dễ hiểu","easyToUnderstand","thumb-up"],["Giúp tôi giải quyết được vấn đề","solvedMyProblem","thumb-up"],["Khác","otherUp","thumb-up"]],[["Thiếu thông tin tôi cần","missingTheInformationINeed","thumb-down"],["Quá phức tạp/quá nhiều bước","tooComplicatedTooManySteps","thumb-down"],["Đã lỗi thời","outOfDate","thumb-down"],["Vấn đề về bản dịch","translationIssue","thumb-down"],["Vấn đề về mẫu/mã","samplesCodeIssue","thumb-down"],["Khác","otherDown","thumb-down"]],["Cập nhật lần gần đây nhất: 2025-07-27 UTC."],[],[],null,["# Display paged lists\n\nThis guide builds upon the [Paging Library\noverview](/topic/libraries/architecture/paging), describing how you can\npresent lists of information to users in your app's UI, particularly when this\ninformation changes.\n| **Caution:** This guide covers an older, deprecated version of the Paging library. For more information about the latest stable version of Paging, see the [Paging\n| 3 guides](/topic/libraries/architecture/paging/v3-overview).\n\nConnect your UI to your view model\n----------------------------------\n\nYou can connect an instance of\n[`LiveData\u003cPagedList\u003e`](/reference/androidx/lifecycle/LiveData) to a\n[`PagedListAdapter`](/reference/androidx/paging/PagedListAdapter), as shown\nin the following code snippet: \n\n### Kotlin\n\n```kotlin\nclass ConcertActivity : AppCompatActivity() {\n private val adapter = ConcertAdapter()\n\n // Use the 'by viewModels()' Kotlin property delegate\n // from the activity-ktx artifact\n private val viewModel: ConcertViewModel by viewModels()\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState);\n viewModel.concerts.observe(this, Observer { adapter.submitList(it) })\n }\n}\n```\n\n### Java\n\n```java\npublic class ConcertActivity extends AppCompatActivity {\n private ConcertAdapter adapter = new ConcertAdapter();\n private ConcertViewModel viewModel;\n\n @Override\n public void onCreate(@Nullable Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n viewModel = new ViewModelProvider(this).get(ConcertViewModel.class);\n viewModel.concertList.observe(this, adapter::submitList);\n }\n}\n```\n\nAs data sources provide new instances of\n[`PagedList`](/reference/androidx/paging/PagedList), the activity sends\nthese objects to the adapter. The\n[`PagedListAdapter`](/reference/androidx/paging/PagedListAdapter)\nimplementation defines how updates are computed, and it automatically handles\npaging and list diffing. Therefore, your\n[`ViewHolder`](/reference/androidx/recyclerview/widget/RecyclerView.ViewHolder)\nonly needs to bind to a particular provided item: \n\n### Kotlin\n\n```kotlin\nclass ConcertAdapter() :\n PagedListAdapter\u003cConcert, ConcertViewHolder\u003e(DIFF_CALLBACK) {\n override fun onBindViewHolder(holder: ConcertViewHolder, position: Int) {\n val concert: Concert? = getItem(position)\n\n // Note that \"concert\" is a placeholder if it's null.\n holder.bindTo(concert)\n }\n\n companion object {\n private val DIFF_CALLBACK = ... // See #implement-diffing-callback section.\n }\n}\n```\n\n### Java\n\n```java\npublic class ConcertAdapter\n extends PagedListAdapter\u003cConcert, ConcertViewHolder\u003e {\n protected ConcertAdapter() {\n super(DIFF_CALLBACK);\n }\n\n @Override\n public void onBindViewHolder(@NonNull ConcertViewHolder holder,\n int position) {\n Concert concert = getItem(position);\n\n // Note that \"concert\" can be null if it's a placeholder.\n holder.bindTo(concert);\n }\n\n private static DiffUtil.ItemCallback\u003cConcert\u003e DIFF_CALLBACK\n = ... // See #implement-diffing-callback section.\n}\n```\n\nThe [`PagedListAdapter`](/reference/androidx/paging/PagedListAdapter)\nhandles page load events using a\n[`PagedList.Callback`](/reference/androidx/paging/PagedList.Callback)\nobject. As the user scrolls, the `PagedListAdapter` calls\n[`PagedList.loadAround()`](/reference/androidx/paging/PagedList#loadaround)\nto provide hints to the underlying\n[`PagedList`](/reference/androidx/paging/PagedList) as to\nwhich items it should fetch from the\n[`DataSource`](/reference/androidx/paging/DataSource).\n| **Note:** [`PagedList`](/reference/androidx/paging/PagedList) is content-immutable. This means that, although new content can be loaded into an instance of `PagedList`, the loaded items themselves cannot change once loaded. As such, if content in a `PagedList` updates, the [`PagedListAdapter`](/reference/androidx/paging/PagedListAdapter) object receives a completely new `PagedList` that contains the updated information.\n\nImplement the diffing callback\n------------------------------\n\nThe following sample shows a manual implementation of\n[`areContentsTheSame()`](/reference/androidx/recyclerview/widget/DiffUtil.ItemCallback#arecontentsthesame),\nwhich compares relevant object fields: \n\n### Kotlin\n\n```kotlin\nprivate val DIFF_CALLBACK = object : DiffUtil.ItemCallback\u003cConcert\u003e() {\n // The ID property identifies when items are the same.\n override fun areItemsTheSame(oldItem: Concert, newItem: Concert) =\n oldItem.id == newItem.id\n\n // If you use the \"==\" operator, make sure that the object implements\n // .equals(). Alternatively, write custom data comparison logic here.\n override fun areContentsTheSame(\n oldItem: Concert, newItem: Concert) = oldItem == newItem\n}\n```\n\n### Java\n\n```java\nprivate static DiffUtil.ItemCallback\u003cConcert\u003e DIFF_CALLBACK =\n new DiffUtil.ItemCallback\u003cConcert\u003e() {\n\n @Override\n public boolean areItemsTheSame(Concert oldItem, Concert newItem) {\n // The ID property identifies when items are the same.\n return oldItem.getId() == newItem.getId();\n }\n\n @Override\n public boolean areContentsTheSame(Concert oldItem, Concert newItem) {\n // Don't use the \"==\" operator here. Either implement and use .equals(),\n // or write custom data comparison logic here.\n return oldItem.equals(newItem);\n }\n};\n```\n\nBecause your adapter includes your definition of comparing items, the adapter\nautomatically detects changes to these items when a new `PagedList` object is\nloaded. As a result, the adapter triggers efficient item animations within your\n`RecyclerView` object.\n\nDiffing using a different adapter type\n--------------------------------------\n\nIf you choose not to inherit from\n[`PagedListAdapter`](/reference/androidx/paging/PagedListAdapter)---such as\nwhen you're using a library that provides its own adapter---you can still use the\nPaging Library adapter's diffing functionality by working directly with an\n[`AsyncPagedListDiffer`](/reference/androidx/paging/AsyncPagedListDiffer)\nobject.\n\nProvide placeholders in your UI\n-------------------------------\n\nIn cases where you want your UI to display a list before your app has finished\nfetching data, you can show placeholder list items to your users. The\n[`PagedList`](/reference/androidx/paging/PagedList) handles this case by\npresenting the list item data as `null` until the data is loaded.\n| **Note:** By default, the Paging Library enables this placeholder behavior.\n\nPlaceholders have the following benefits:\n\n- **Support for scrollbars:** The [`PagedList`](/reference/androidx/paging/PagedList) provides the number of list items to the [`PagedListAdapter`](/reference/androidx/paging/PagedListAdapter). This information allows the adapter to draw a scrollbar that conveys the full size of the list. As new pages load, the scrollbar doesn't jump because your list doesn't change size.\n- **No loading spinner necessary:** Because the list size is already known, there's no need to alert users that more items are loading. The placeholders themselves convey that information.\n\nBefore adding support for placeholders, though, keep the following\npreconditions in mind:\n\n- **Requires a countable data set:** Instances of [`DataSource`](/reference/androidx/paging/DataSource) from the [Room\n persistence library](/topic/libraries/architecture/room) can efficiently count their items. If you're using a custom local storage solution or a [network-only\n data architecture](/topic/libraries/architecture/paging#network-only-data-arch), however, it might be expensive or even impossible to determine how many items comprise your data set.\n- **Requires adapter to account for unloaded items:** The adapter or presentation mechanism that you use to prepare the list for inflation needs to handle null list items. For example, when binding data to a [`ViewHolder`](/reference/androidx/recyclerview/widget/RecyclerView.ViewHolder), you need to provide default values to represent unloaded data.\n- **Requires same-sized item views:** If list item sizes can change based on their content, such as social networking updates, crossfading between items doesn't look good. We strongly suggest disabling placeholders in this case.\n\nProvide feedback\n----------------\n\nShare your feedback and ideas with us through these resources:\n\n[Issue tracker](https://issuetracker.google.com/issues/new?component=413106&template=1096385) :bug:\n: Report issues so we can fix bugs.\n\nAdditional resources\n--------------------\n\nTo learn more about the Paging Library, consult the\nfollowing resources.\n\n### Samples\n\n- [Android Architecture Components Paging\n sample](https://github.com/android/architecture-components-samples/tree/paging2/PagingSample)\n- [Paging With Network Sample](https://github.com/android/architecture-components-samples/tree/paging2/PagingWithNetworkSample)\n\n### Codelabs\n\n- [Android Paging codelab](https://codelabs.developers.google.com/codelabs/android-paging/index.html?index=..%2F..%2Findex#0)\n\n### Videos\n\n- [Android Jetpack: manage infinite lists with RecyclerView and Paging\n (Google I/O '18)](https://www.youtube.com/watch?v=BE5bsyGGLf4)\n- [Android Jetpack: Paging](https://www.youtube.com/watch?v=QVMqCRs0BNA)\n\nRecommended for you\n-------------------\n\n- Note: link text is displayed when JavaScript is off\n- [Paging 2 library overview](/topic/libraries/architecture/paging)\n- [Migrate to Paging 3](/topic/libraries/architecture/paging/v3-migration)\n- [Gather paged data](/topic/libraries/architecture/paging/data)"]]