All Downloads are FREE. Search and download functionalities are using the official Maven repository.

androidx.paging.PagedListAdapter.txt Maven / Gradle / Ivy

There is a newer version: 8.2.0-rc.4
Show newest version
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.paging;
import android.annotation.SuppressLint;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.AdapterListUpdateCallback;
import androidx.recyclerview.widget.AsyncDifferConfig;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
/**
 * {@link RecyclerView.Adapter RecyclerView.Adapter} base class for presenting paged data from
 * {@link PagedList}s in a {@link RecyclerView}.
 * 

* This class is a convenience wrapper around {@link AsyncPagedListDiffer} that implements common * default behavior for item counting, and listening to PagedList update callbacks. *

* While using a LiveData<PagedList> is an easy way to provide data to the adapter, it isn't * required - you can use {@link #submitList(PagedList)} when new lists are available. *

* PagedListAdapter listens to PagedList loading callbacks as pages are loaded, and uses DiffUtil on * a background thread to compute fine grained updates as new PagedLists are received. *

* Handles both the internal paging of the list as more data is loaded, and updates in the form of * new PagedLists. *

* A complete usage pattern with Room would look like this: *

 * {@literal @}Dao
 * interface UserDao {
 *     {@literal @}Query("SELECT * FROM user ORDER BY lastName ASC")
 *     public abstract DataSource.Factory<Integer, User> usersByLastName();
 * }
 *
 * class MyViewModel extends ViewModel {
 *     public final LiveData<PagedList<User>> usersList;
 *     public MyViewModel(UserDao userDao) {
 *         usersList = new LivePagedListBuilder<>(
 *                 userDao.usersByLastName(), /* page size {@literal *}/ 20).build();
 *     }
 * }
 *
 * class MyActivity extends AppCompatActivity {
 *     {@literal @}Override
 *     public void onCreate(Bundle savedState) {
 *         super.onCreate(savedState);
 *         MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
 *         RecyclerView recyclerView = findViewById(R.id.user_list);
 *         UserAdapter<User> adapter = new UserAdapter();
 *         viewModel.usersList.observe(this, pagedList -> adapter.submitList(pagedList));
 *         recyclerView.setAdapter(adapter);
 *     }
 * }
 *
 * class UserAdapter extends PagedListAdapter<User, UserViewHolder> {
 *     public UserAdapter() {
 *         super(DIFF_CALLBACK);
 *     }
 *     {@literal @}Override
 *     public void onBindViewHolder(UserViewHolder holder, int position) {
 *         User user = getItem(position);
 *         if (user != null) {
 *             holder.bindTo(user);
 *         } else {
 *             // Null defines a placeholder item - PagedListAdapter will automatically invalidate
 *             // this row when the actual object is loaded from the database
 *             holder.clear();
 *         }
 *     }
 *     public static final DiffUtil.ItemCallback<User> DIFF_CALLBACK =
 *             new DiffUtil.ItemCallback<User>() {
 *         {@literal @}Override
 *         public boolean areItemsTheSame(
 *                 {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
 *             // User properties may have changed if reloaded from the DB, but ID is fixed
 *             return oldUser.getId() == newUser.getId();
 *         }
 *         {@literal @}Override
 *         public boolean areContentsTheSame(
 *                 {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
 *             // NOTE: if you use equals, your object must properly override Object#equals()
 *             // Incorrectly returning false here will result in too many animations.
 *             return oldUser.equals(newUser);
 *         }
 *     }
 * }
* * Advanced users that wish for more control over adapter behavior, or to provide a specific base * class should refer to {@link AsyncPagedListDiffer}, which provides the mapping from paging * events to adapter-friendly callbacks. * * @param Type of the PagedLists this Adapter will receive. * @param A class that extends ViewHolder that will be used by the adapter. */ public abstract class PagedListAdapter extends RecyclerView.Adapter { final AsyncPagedListDiffer mDiffer; private final AsyncPagedListDiffer.PagedListListener mListener = new AsyncPagedListDiffer.PagedListListener() { @Override public void onCurrentListChanged( @Nullable PagedList previousList, @Nullable PagedList currentList) { PagedListAdapter.this.onCurrentListChanged(currentList); PagedListAdapter.this.onCurrentListChanged(previousList, currentList); } }; private final PagedList.LoadStateListener mLoadStateListener = new PagedList.LoadStateListener() { @Override public void onLoadStateChanged(@NonNull PagedList.LoadType type, @NonNull PagedList.LoadState state, @Nullable Throwable error) { PagedListAdapter.this.onLoadStateChanged(type, state, error); } }; /** * Creates a PagedListAdapter with default threading and * {@link androidx.recyclerview.widget.ListUpdateCallback}. * * Convenience for {@link #PagedListAdapter(AsyncDifferConfig)}, which uses default threading * behavior. * * @param diffCallback The {@link DiffUtil.ItemCallback DiffUtil.ItemCallback} instance to * compare items in the list. */ protected PagedListAdapter(@NonNull DiffUtil.ItemCallback diffCallback) { mDiffer = new AsyncPagedListDiffer<>(this, diffCallback); mDiffer.addPagedListListener(mListener); mDiffer.addLoadStateListener(mLoadStateListener); } protected PagedListAdapter(@NonNull AsyncDifferConfig config) { mDiffer = new AsyncPagedListDiffer<>(new AdapterListUpdateCallback(this), config); mDiffer.addPagedListListener(mListener); mDiffer.addLoadStateListener(mLoadStateListener); } /** * Set the new list to be displayed. *

* If a list is already being displayed, a diff will be computed on a background thread, which * will dispatch Adapter.notifyItem events on the main thread. * * @param pagedList The new list to be displayed. */ public void submitList(@Nullable PagedList pagedList) { mDiffer.submitList(pagedList); } /** * Set the new list to be displayed. *

* If a list is already being displayed, a diff will be computed on a background thread, which * will dispatch Adapter.notifyItem events on the main thread. *

* The commit callback can be used to know when the PagedList is committed, but note that it * may not be executed. If PagedList B is submitted immediately after PagedList A, and is * committed directly, the callback associated with PagedList A will not be run. * * @param pagedList The new list to be displayed. * @param commitCallback Optional runnable that is executed when the PagedList is committed, if * it is committed. */ public void submitList(@Nullable PagedList pagedList, @Nullable final Runnable commitCallback) { mDiffer.submitList(pagedList, commitCallback); } @Nullable protected T getItem(int position) { return mDiffer.getItem(position); } @Override public int getItemCount() { return mDiffer.getItemCount(); } /** * Returns the PagedList currently being displayed by the Adapter. *

* This is not necessarily the most recent list passed to {@link #submitList(PagedList)}, * because a diff is computed asynchronously between the new list and the current list before * updating the currentList value. May be null if no PagedList is being presented. * * @return The list currently being displayed. * * @see #onCurrentListChanged(PagedList, PagedList) */ @Nullable public PagedList getCurrentList() { return mDiffer.getCurrentList(); } /** * Called when the current PagedList is updated. *

* This may be dispatched as part of {@link #submitList(PagedList)} if a background diff isn't * needed (such as when the first list is passed, or the list is cleared). In either case, * PagedListAdapter will simply call * {@link #notifyItemRangeInserted(int, int) notifyItemRangeInserted/Removed(0, mPreviousSize)}. *

* This method will notbe called when the Adapter switches from presenting a PagedList * to a snapshot version of the PagedList during a diff. This means you cannot observe each * PagedList via this method. * * @deprecated Use the two argument variant instead: * {@link #onCurrentListChanged(PagedList, PagedList)} * * @param currentList new PagedList being displayed, may be null. * * @see #getCurrentList() */ @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated public void onCurrentListChanged(@Nullable PagedList currentList) { } /** * Called when the current PagedList is updated. *

* This may be dispatched as part of {@link #submitList(PagedList)} if a background diff isn't * needed (such as when the first list is passed, or the list is cleared). In either case, * PagedListAdapter will simply call * {@link #notifyItemRangeInserted(int, int) notifyItemRangeInserted/Removed(0, mPreviousSize)}. *

* This method will notbe called when the Adapter switches from presenting a PagedList * to a snapshot version of the PagedList during a diff. This means you cannot observe each * PagedList via this method. * * @param previousList PagedList that was previously displayed, may be null. * @param currentList new PagedList being displayed, may be null. * * @see #getCurrentList() */ public void onCurrentListChanged( @Nullable PagedList previousList, @Nullable PagedList currentList) { } /** * Called when the LoadState for a particular type of load (START, END, REFRESH) has * changed. *

* REFRESH events can be used to drive a {@code SwipeRefreshLayout}, or START/END events * can be used to drive loading spinner items in the Adapter. * * @param type Type of load - START, END, or REFRESH. * @param state State of load - IDLE, LOADING, DONE, ERROR, or RETRYABLE_ERROR * @param error Error, if in an error state, null otherwise. */ public void onLoadStateChanged(@NonNull PagedList.LoadType type, @NonNull PagedList.LoadState state, @Nullable Throwable error) { } /** * Add a LoadStateListener to observe the loading state of the current PagedList. * * As new PagedLists are submitted and displayed, the listener will be notified to reflect * current REFRESH, START, and END states. * * @param listener Listener to receive updates. * * @see #removeLoadStateListener(PagedList.LoadStateListener) */ @SuppressLint("UnknownNullness") public void addLoadStateListener(PagedList.LoadStateListener listener) { mDiffer.addLoadStateListener(listener); } /** * Remove a previously registered LoadStateListener. * * @param listener Previously registered listener. * @see #addLoadStateListener(PagedList.LoadStateListener) */ @SuppressLint("UnknownNullness") public void removeLoadStateListener(PagedList.LoadStateListener listener) { mDiffer.removeLoadStateListListener(listener); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy