RecyclerView簡單案例&新增下拉重新整理(SwipeRefreshLayout)、上拉載入(lastVisibleItem)
效果圖如下
一、先看各個佈局
activity_main
使用SwipeRefreshLayout包裹RecyclerView<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <android.support
list_item
只顯示一個一個字串<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"
list_foot
最後一個條目可見時載入更多資料(一個loading和文字提示)<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:orientation="horizontal" android:padding="10dp"> <ProgressBar android:id="@+id/pb_loading" style="@android:style/Widget.ProgressBar.Inverse" android:layout_width="26dp" android:layout_height="26dp" android:layout_marginLeft="130dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:text="鬆開載入" android:textColor="#c0f0" android:textSize="20sp"/> </LinearLayout>
二、介面卡的實現(★)
RecyclerView的介面卡(要熟練掌握哦)
分析資料應該如何展示,完整程式碼緊跟在分析之後怎麼區分當前位置要展示的具體佈局型別
ListView中我們很熟悉了,通過getViewTypeCount(): Int
獲取需要展示的佈局數目(如2),然後通過getItemViewType(): Int
獲取當前位置需要展示的具體佈局型別
↑ VS ↓
RecyclerView中僅使用getItemViewType(): Int
即可獲得當前位置對應的佈局型別,用法和ListView的getItemViewType(): Int
別無它異,具體參考下面的完整程式碼ViewHolder如何建立
ListView中需要手動建立靜態Holder,並在getView(): View
中先後手動setTag()
和getTag()
來獲取holder,然後進行控制元件內容的填充
↑ VS ↓
RecyclerView已幫我們整合進了ViewHolder(這也是Recycler的核心),只要繼承RecyclerView.Adapter
時新增泛型RecyclerView.ViewHolder
,然後分別新增各佈局對應的Holder類(類中初始化控制元件),最後在onCreateViewHolder: RecyclerView.ViewHolder
中通過各佈局型別建立對應的Holder例項即可如何通過ViewHolder填充資料
ListView中在getView(): View
的快取複用、holder的setTag()
完成後直接進行控制元件內容的填充
↑ VS ↓
RecyclerView中,我們單獨在onBindViewHolder(holder): void
方法中通過轉換holder型別後進行控制元件內容的填充
public class SampleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { // 要展示的資料集合 private List<String> mDatas; // 判斷佈局型別 private static final int TYPE_ITEM = 0; private static final int TYPE_FOOT = 1; /** * 初始化工作(在這裡是獲得了資料集合) */ public SampleAdapter(List<String> datas) { mDatas = datas; } /** * 當前位置應該展示的條目佈局的型別 */ @Override public int getItemViewType(int position) { // 資料集合最後一行資料之後的那一行就應該載入腳佈局 if (position + 1 == getItemCount()) { return TYPE_FOOT; } else { return TYPE_ITEM; } } /** * 設定各佈局,並返回與其對應的ViewHolder例項 */ @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = null; RecyclerView.ViewHolder holder = null; if (viewType == TYPE_ITEM) { view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, null); holder = new ItemHolder(view); } else if (viewType == TYPE_FOOT) { view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_foot, null); holder = new FootHolder(view); } else { // 預設的佈局及其對應的ViewHolder例項,防止發生意外 // default view = ... // default holder = ... } return holder; } /** * 填充各佈局的控制元件內容 */ @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { if (holder instanceof ItemHolder) { ((ItemHolder) holder).tvText.setText(String.valueOf(mDatas.get(position))); } } /** * 要展示的條目總數(包括所有資料集合和額外新增的頭、身、腳條目數量) */ @Override public int getItemCount() { return mDatas.size() + 1; } /**************************************** * 各佈局型別的ViewHolder */ class FootHolder extends RecyclerView.ViewHolder { public FootHolder(View view) { super(view); } } class ItemHolder extends RecyclerView.ViewHolder { // 展示一行字串 @BindView(R.id.tv_text) TextView tvText; public ItemHolder(View view) { super(view); ButterKnife.bind(this, view); } } }
三、Activity中如何展示並新增重新整理和載入邏輯
注意資料集合發生變化,需要使用
mAdapter.notifyItemRemoved(position)
來告知介面卡以進行介面重新整理
public class MainActivity extends BaseActivity {
@BindView(R.id.srl) SwipeRefreshLayout srl;
@BindView(R.id.rv) RecyclerView rv;
/**
* 佈局管理器
* 1. LinearLayoutManager:支援橫向、縱向
* 2. GridLayoutManager:網格
* 3. StaggeredGridLayoutManager:瀑布流
*/
private LinearLayoutManager mLayoutManager;
private List<String> mDatas;
private SampleAdapter mAdapter;
// 記錄最後可見條目
private int lastVisible;
// 分別記錄重新整理和載入次數
int refreshTimes = 0;
int addmoreTimes = 0;
@Override int layoutId() {
return R.layout.activity_main;
}
/**
* 初始化資料
*/
@Override void initData() {
mDatas = new ArrayList<String>();
for (int i = 0; i < 15; i++) {
mDatas.add("資料== " + (i + 1) + " ==");
}
}
@Override void initView() {
srl.setColorSchemeResources(R.color.color1, R.color.color2, R.color.color3, R.color.color4);
srl.setOnRefreshListener(this);
rv.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
// 不在滑動和最後可見條目是腳佈局時載入更多
if (newState == RecyclerView.SCROLL_STATE_IDLE && lastVisible + 1 == mAdapter.getItemCount()) {
addmore();
}
}
@Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
lastVisible = mLayoutManager.findLastVisibleItemPosition();
}
});
rv.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
rv.setLayoutManager(mLayoutManager);
rv.setItemAnimator(new DefaultItemAnimator());
mAdapter = new SampleAdapter(mDatas);
rv.setAdapter(mAdapter);
}
/**
* 模仿下拉重新整理
*/
@Override void srlRefresh() {
mDatas.add(0, "重新整理 " + ++refreshTimes + "次");
new Timer().schedule(new TimerTask() {
@Override public void run() {
runOnUiThread(new Runnable() {
@Override public void run() {
Toast.makeText(MainActivity.this, "重新整理" + refreshTimes, Toast.LENGTH_SHORT).show();
srl.setRefreshing(false);
mAdapter.notifyDataSetChanged();
}
});
}
}, 1000);
}
/**
* 模仿上拉載入
*/
void addmore() {
new Timer().schedule(new TimerTask() {
@Override public void run() {
runOnUiThread(new Runnable() {
@Override public void run() {
mDatas.add("資料增加" + ++addmoreTimes);
Toast.makeText(MainActivity.this, "載入" + addmoreTimes, Toast.LENGTH_SHORT).show();
mAdapter.notifyItemInserted(mDatas.size());
}
});
}
}, 1000);
}
}