為什么分頁锣枝?
從開發(fā)者的角度來看,如何加載所有內(nèi)容拴鸵?一次不可能顯示很多的內(nèi)容玷坠。我們只能顯示它們的部分蜗搔。
分頁允許用戶看到最新的內(nèi)容,等待時間很少八堡。當(dāng)我們在用戶滾動到底部時加載下一個“頁面”樟凄,更多的內(nèi)容被加載并可用。
何時使用分頁兄渺?
如果你有大量的內(nèi)容需要太長時間才能加載缝龄。這可以是本地數(shù)據(jù)庫或API調(diào)用。那么使用分頁是有意義的挂谍。如果您從數(shù)據(jù)庫中提取數(shù)據(jù)叔壤,請分批請求數(shù)據(jù)(每個請求20個)。同樣的情況也適用于API調(diào)用口叙。
Android分頁與RecyclerView
① 自定義OnScrollListener
public abstract class PaginationScrollListener extends RecyclerView.OnScrollListener {
LinearLayoutManager layoutManager;
public PaginationScrollListener(LinearLayoutManager layoutManager) {
this.layoutManager = layoutManager;
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int visibleItemCount = layoutManager.getChildCount();
int totalItemCount = layoutManager.getItemCount();
int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();
if (!isLoading() && !isLastPage()) {
if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount
&& firstVisibleItemPosition >= 0) {
loadMoreItems();
}
}
}
protected abstract void loadMoreItems();
public abstract int getTotalPageCount();
public abstract boolean isLastPage();
public abstract boolean isLoading();
}
如果要啟用分頁炼绘,我們必須要檢測達(dá)到列表(RecyclerView)的結(jié)尾。PaginationScrollListener才能啟用分頁妄田。
② 布局設(shè)置
創(chuàng)建一個布局RecyclerView和一個ProgressBar(用于指示初始內(nèi)容的加載)俺亮。
<FrameLayout>
<android.support.v7.widget.RecyclerView />
<ProgressBar android:layout_gravity="center”/>
</FrameLayout>
③ 創(chuàng)建RecyclerView.Adapter
首先,創(chuàng)建類PaginationAdapter擴(kuò)展RecyclerView.Adapter疟呐,然后創(chuàng)建兩個RecyclerView.ViewHolder铅辞。
1、class ContentVH (main content item)
2萨醒、class LoadingVH (footer ProgressBar used for Pagination)
public class PaginationAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
// flag for footer ProgressBar
private boolean isLoadingAdded = false;
...
@Override
public int getItemCount() {
return movies == null ? 0 : movies.size();
}
@Override
public int getItemViewType(int position) {
return (position == movies.size() - 1 && isLoadingAdded) ? LOADING : ITEM;
}
...
}
對于我們的例子斟珊,我們假設(shè)要顯示一個電影列表。
④ Adapter的輔助方法
將以下方法添加到PaginationAdapter中富纸。它們對于通過分頁獲取的添加數(shù)據(jù)很有用囤踩。
public void add(Movie mc) {
movies.add(mc);
notifyItemInserted(movies.size() - 1);
}
public void addAll(List<Movie> mcList) {
for (Movie mc : mcList) {
add(mc);
}
}
public void remove(Movie city) {
int position = movies.indexOf(city);
if (position > -1) {
movies.remove(position);
notifyItemRemoved(position);
}
}
public void clear() {
isLoadingAdded = false;
while (getItemCount() > 0) {
remove(getItem(0));
}
}
public boolean isEmpty() {
return getItemCount() == 0;
}
public void addLoadingFooter() {
isLoadingAdded = true;
add(new Movie());
}
public void removeLoadingFooter() {
isLoadingAdded = false;
int position = movies.size() - 1;
Movie item = getItem(position);
if (item != null) {
movies.remove(position);
notifyItemRemoved(position);
}
}
public Movie getItem(int position) {
return movies.get(position);
}
⑤ 初始化RECYCLERVIEW
PaginationAdapter adapter = new PaginationAdapter(this);
linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
rv.setLayoutManager(linearLayoutManager);
rv.setItemAnimator(new DefaultItemAnimator());
rv.setAdapter(adapter);
到這里,我們來介紹一下分頁在此流程中如何工作的:
1晓褪、ProgressDialog在取得初始數(shù)據(jù)的同時在空白屏幕上顯示加載進(jìn)度
2堵漱、隱藏ProgressDialog和顯示數(shù)據(jù)
3、檢測用戶滾動到列表的末尾
4涣仿、ProgressDialog在提取下一頁數(shù)據(jù)時在頁腳顯示
5勤庐、刪除頁腳ProgressDialog并顯示提取的數(shù)據(jù)
6、重復(fù)步驟3,4和5好港,直到所有頁面都已加載
Activity設(shè)置
public class MainActivity extends AppCompatActivity {
PaginationAdapter adapter;
LinearLayoutManager linearLayoutManager;
RecyclerView rv;
ProgressBar progressBar;
// 分頁開始的索引(0是我們的第一頁)
private static final int PAGE_START = 0;
//表示是否顯示了頁腳ProgressBar(即下一頁正在加載)
private boolean isLoading = false;
// 如果當(dāng)前頁面是最后一頁(頁面加載后分頁將停止)
private boolean isLastPage = false;
//總的頁面加載數(shù)愉镰。初始加載為第0頁,之后再加載2頁钧汹。
private int TOTAL_PAGES = 3;
// 表示分頁正在加載的頁面丈探。
private int currentPage = PAGE_START;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
rv = (RecyclerView) findViewById(R.id.main_recycler);
progressBar = (ProgressBar) findViewById(R.id.main_progress);
adapter = new PaginationAdapter(this);
linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
rv.setLayoutManager(linearLayoutManager);
rv.setItemAnimator(new DefaultItemAnimator());
rv.setAdapter(adapter);
rv.addOnScrollListener(new PaginationScrollListener(linearLayoutManager) {
@Override
protected void loadMoreItems() {
isLoading = true;
currentPage += 1; //增加頁面索引以加載下一個
loadNextPage();
}
@Override
public int getTotalPageCount() {
return TOTAL_PAGES;
}
@Override
public boolean isLastPage() {
return isLastPage;
}
@Override
public boolean isLoading() {
return isLoading;
}
});
loadFirstPage();
}
...
}
加載初始數(shù)據(jù)
以下是我們?nèi)绾问褂迷摲椒▓?zhí)行初始加載(即第一頁請求):
private void loadFirstPage() {
////獲取虛擬數(shù)據(jù)
List<Movie> movies = Movie.createMovies(adapter.getItemCount());
progressBar.setVisibility(View.GONE);
adapter.addAll(movies);
if (currentPage <= TOTAL_PAGES) adapter.addLoadingFooter();
else isLastPage = true;
}
我們甚至可以使用Handler模??擬網(wǎng)絡(luò)延遲。
// 模擬1秒網(wǎng)絡(luò)延遲
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
loadFirstPage();
}
}, 1000);
一旦我們加載初始請求并獲取數(shù)據(jù)拔莱,請隱藏ProgressBar碗降。接下來隘竭,將獲取的數(shù)據(jù)添加到適配器并通知更新。PaginationAdapter中的addAll()方法完成了此操作讼渊。
使用Paginati onScrollListener
請注意PaginationScrollListener如何使用我們Activity中定義的標(biāo)志动看。它需要LayoutManager提供給RecyclerView來計數(shù)和比較它的數(shù)量。這更準(zhǔn)確地知道在布局中實際有多少項目爪幻,而不是計算List <Model>弧圆。但是現(xiàn)在,它的構(gòu)造函數(shù)只支持LinearLayoutManager笔咽。
一旦初始數(shù)據(jù)加載搔预,它的時間傾聽滾動更改并觸發(fā)下一頁
private void loadNextPage() {
List<Movie> movies = Movie.createMovies(adapter.getItemCount()); // 1
adapter.removeLoadingFooter(); // 2
isLoading = false; // 3
adapter.addAll(movies); // 4
if (currentPage != TOTAL_PAGES) adapter.addLoadingFooter(); // 5
else isLastPage = true;
}
最終結(jié)果
根據(jù)我們的代碼設(shè)置,我們每頁加載10個虛擬內(nèi)容叶组。分頁將發(fā)生的次數(shù)為3(TOTAL_PAGE)拯田。添加初始頁面加載,您正在查看40個項目甩十。
到這里咱們就結(jié)束了船庇。