先上效果圖, Material Design風格的下拉刷新和上拉載入不少其它。html
源代碼地址(歡迎star) https://github.com/studychen/SeeNewsV2java
假設對於RecyclerView還不熟悉,參見這篇 Android Material Design學習之RecyclerView取代 ListViewandroid
本文連接 http://blog.csdn.net/never_cxb/article/details/50759109 轉載請註明出處git
上拉時候會有一個圓形動畫。刷新載入數據。github
使用Google官方的android.support.v4.widget.SwipeRefreshLayout
數組
給原來的RecyclerView添加一個SwipeRefreshLayout的父佈局markdown
dom
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="?less
attr/listBackground" android:orientation="vertical"> <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swiperefreshlayout" android:layout_width="match_parent" android:layout_height="wrap_content"> <!-- 新聞列表展現--> <android.support.v7.widget.RecyclerView android:id="@+id/rcv_article_origin" android:layout_width="match_parent" android:layout_height="match_parent" /> </android.support.v4.widget.SwipeRefreshLayout> </LinearLayout>
這兒有幾個注意點:
setColorSchemeColors()
可以控制圓形動畫的顏色,最多設置4個。ide
setOnRefreshListener
設置下拉刷新的回調事件。
下拉刷新後。使用 AsyncTask 依據當前RecyclerView中首個Item的id來載入不少其它數據。
數據載入完成後,使用setRefreshing(false);
取消動畫。
假設刷新後獲得0條記錄,提示沒有數據更新
。若獲得>0條數據。把數據加到RecyclerView中
mSwipeRefreshLayout.setColorSchemeColors(Color.RED, Color.BLUE);
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
new MoreArticleTask().execute(mAdapter.getTopArticleId());
}
});
// Integer 是輸入參數
// 獲得比某個id大的新聞數組
class MoreArticleTask extends AsyncTask<Integer, Void, List<SimpleArticleItem>> {
@Override
protected List<SimpleArticleItem> doInBackground(Integer... params) {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return getMoreById(mColumn, params[0]);
}
@Override
protected void onPostExecute(List<SimpleArticleItem> simpleArticleItems) {
super.onPostExecute(simpleArticleItems);
if (mSwipeRefreshLayout != null) {
mSwipeRefreshLayout.setRefreshing(false);
}
//沒有新的數據。提示消息
if (simpleArticleItems == null || simpleArticleItems.size() == 0) {
Snackbar.with(mActivity.getApplicationContext()) // context
.text(mActivity.getResources().getString(R.string.list_more_data)) // text to display
.duration(Snackbar.SnackbarDuration.LENGTH_SHORT) // make it shorter
.show(mActivity); // activity where it is displayed
} else {
mArticleList.addAll(simpleArticleItems);
mAdapter.notifyDataSetChanged();
}
}
}
效果例如如下:
直接使用 mSwipeRefreshLayout.setRefreshing(true);
載入動畫初始狀態並不顯示。
看了 http://stackoverflow.com/questions/26858692/swiperefreshlayout-setrefreshing-not-showing-indicator-initially 的解答。
改用如下的代碼,初始狀態就有載入動畫顯示。
mSwipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
mSwipeRefreshLayout.setRefreshing(true);
new MoreArticleTask().execute(mAdapter.getTopArticleId());
}
});
RecyclerView 展現列表是個通用需求。那麼當數據較多時候。怎樣實現分頁載入呢?
比方筆者的項目中,先載入15條新聞,當滑動究竟部時候,再載入15條。
在RecyclerView底部添加一個Footer的ViewHolder。數據載入完成取出底部的ViewHolder。
很是easy,就是一個ProgressBar。本項目爲了Material Design。使用了一個開源ProgressBar https://github.com/Todd-Davies/ProgressWheel,你也可以使用原生的ProgressBar。
<?xml version="1.0" encoding="utf-8"?><!--上拉載入不少其它 RecyclerView 底部--> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:wheel="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.pnikosis.materialishprogress.ProgressWheel android:id="@+id/rcv_load_more" android:layout_width="40dp" android:layout_height="40dp" android:layout_gravity="center_horizontal" wheel:matProg_barColor="@color/accent" wheel:matProg_progressIndeterminate="true" /> </LinearLayout>
/** * 底部載入不少其它 */
class FooterViewHolder extends RecyclerView.ViewHolder {
@InjectView(R.id.rcv_load_more)
ProgressWheel rcvLoadMore;
public FooterViewHolder(View itemView) {
super(itemView);
ButterKnife.inject(this, itemView);
}
}
layoutManager.getItemCount()
可以獲得當前RecyclerView中Item的總數目
layoutManager.findLastVisibleItemPosition()
獲得最後一個可見的Item的位置position
假設 totalItemCount < (lastVisibleItem + Constant.VISIBLE_THRESHOLD)
比方一共15個Item,當前到達第13個, Constant.VISIBLE_THRESHOLD設爲3
總數小於最後一個+閾值,就載入不少其它新聞數據。同一時候把loading標記爲 true 。
private boolean loading = false;
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mRecyclerView.setLayoutManager(new LinearLayoutManager(mActivity));
mAdapter = new OriginArticleAdapter(mActivity, mArticleList);
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
int totalItemCount = layoutManager.getItemCount();
int lastVisibleItem = layoutManager.findLastVisibleItemPosition();
if (!loading && totalItemCount < (lastVisibleItem + Constant.VISIBLE_THRESHOLD)) {
new ArticleTask(mActivity).execute(mAdapter.getBottomArticleId());
loading = true;
}
}
});
}
咱們依舊使用AsyncTask。
注意 onPreExecute()
給 mArticleList 添加了一個null標記Footer。假設是第一次進入頁面(mArticleList爲空)不需要加Footer。
當數據載入完成後。用 mArticleList.remove(mArticleList.size() - 1);
把最如下的Footer刪除。
再用 mArticleList.addAll(moreArticles);
添加新增的新聞數據。
並用 mAdapter.notifyDataSetChanged();
通知 RecyclerView.Adapter 有數據改變。
private List<SimpleArticleItem> mArticleList = new ArrayList<SimpleArticleItem>();
class ArticleTask extends AsyncTask<Integer, Void, List<SimpleArticleItem>> {
private Context mContext;
public ArticleTask(Context context) {
mContext = context;
}
/** * Runs on the UI thread before {@link #doInBackground}. */
@Override
protected void onPreExecute() {
super.onPreExecute();
if (mArticleList != null && mArticleList.size() > 0) {
mArticleList.add(null);
// notifyItemInserted(int position)。這種方法是在第position位置
// 被插入了一條數據的時候可以使用這種方法刷新,
// 注意這種方法調用後會有插入的動畫,這個動畫可以使用默認的,也可以自定義。mAdapter.notifyItemInserted(mArticleList.size() - 1); } } /** * @param params 偏移量 aid * @return */ @Override protected List<SimpleArticleItem> doInBackground(Integer... params) { try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } return getArticleList(mColumn, params[0]); } @Override protected void onPostExecute(final List<SimpleArticleItem> moreArticles) { // 新增新聞數據 super.onPostExecute(moreArticles); if (mArticleList.size() == 0) { mArticleList.addAll(moreArticles); mAdapter.notifyDataSetChanged(); } else { //刪除 footer mArticleList.remove(mArticleList.size() - 1); mArticleList.addAll(moreArticles); mAdapter.notifyDataSetChanged(); loading = false; } } }
extends RecyclerView.Adapter<RecyclerView.ViewHolder>
假設是null,返回Footer的Type;不然。返回正常新聞的Type。
本文,爲了UI美觀,把新聞分爲兩種:大於3幅圖片 、小於3幅圖片,對應返回不一樣的Type和ViewHolder。
public final static int TYPE_MULTI_IMAGES = 2; // 多個圖片的文章
public final static int TYPE_FOOTER = 3;//底部--每每是loading_more
public final static int TYPE_NORMAL = 1; // 正常的一條文章
@Override
public int getItemViewType(int position) {
SimpleArticleItem article = articleList.get(position);
if (article == null) {
return TYPE_FOOTER;
} else if (article.getImageUrls().length >= 3) {
return TYPE_MULTI_IMAGES;
} else {
return TYPE_NORMAL;
}
}
在該方法中利用 switch (viewType)
返回不一樣的xml佈局文件及ViewHolder
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder vh;
View view;
switch (viewType) {
//其它沒法處理的狀況使用viewholder_article_simple
default:
case TYPE_NORMAL:
view = mLayoutInflater.inflate(
R.layout.item_article_normal, parent, false);
vh = new ItemArticleViewHolder(view);
return vh;
case TYPE_FOOTER:
view = mLayoutInflater.inflate(
R.layout.recyclerview_footer, parent, false);
vh = new FooterViewHolder(view);
return vh;
case TYPE_MULTI_IMAGES:
view = mLayoutInflater.inflate(
R.layout.item_article_multi_images, parent, false);
vh = new MultiImagesViewHolder(view);
return vh;
}
}
利用 instanceof 推斷是何種類型的ViewHolder。來給控件賦值、綁定數據。
注意:因爲Footer是用null表示的,爲了防止NullPointerException,
咱們先用if把FooterViewHolder的狀況處理了。
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
//這時候 article是 null。先把 footer 處理了
if (holder instanceof FooterViewHolder) {
((FooterViewHolder) holder).rcvLoadMore.spin();
return;
}
SimpleArticleItem article = articleList.get(position);
String[] imageUrls = article.getImageUrls();
if (holder instanceof ItemArticleViewHolder) {
ItemArticleViewHolder newHolder = (ItemArticleViewHolder) holder;
newHolder.rcvArticleTitle.setText(article.getTitle());
newHolder.rcvArticleDate.setText(article.getPublishDate());
//當圖片小於3張時候 選取第1張圖片
if (imageUrls.length > 0) {
newHolder.rcvArticlePhoto.setImageURI(Uri.parse(Constant.BUCKET_HOST_NAME
+ imageUrls[0]));
} else {
newHolder.rcvArticlePhoto.setImageURI(Uri.parse(ApiUrl.randomImageUrl(article.getId())));
}
//注意這個閱讀次數是 int 類型。需要轉化爲 String 類型
newHolder.rcvArticleReadtimes.setText("瀏覽: " + article.getReadTimes());
newHolder.rcvArticleSummary.setText(article.getSummary());
} else {
MultiImagesViewHolder newHolder = (MultiImagesViewHolder) holder;
newHolder.articleTitle.setText(article.getTitle());
newHolder.articlePic1.setImageURI(Uri.parse(Constant.BUCKET_HOST_NAME + imageUrls[0]));
newHolder.articlePic2.setImageURI(Uri.parse(Constant.BUCKET_HOST_NAME + imageUrls[1]));
newHolder.articlePic3.setImageURI(Uri.parse(Constant.BUCKET_HOST_NAME + imageUrls[2]));
newHolder.countPics.setText("圖片: " + imageUrls.length);
newHolder.countRead.setText("瀏覽: " + article.getReadTimes());
}
}
下拉刷新和上拉載入不少其它的源代碼地址(歡迎star) https://github.com/studychen/SeeNewsV2
本文連接 http://blog.csdn.net/never_cxb/article/details/50759109 轉載請註明出處
注意給RecyclerView setLayoutManager,否則RecyclerView可能不顯示
// 1. get a reference to recyclerView
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list);
// 2. set layoutManger
recyclerView.setLayoutManager(new LinearLayoutManager(this));