RecyclerView的重新整理分頁
在開發中常常使用到重新整理分頁,這裡實現一個 RecyclerView 的簡單的重新整理分頁操作,測試效果見文末,實現過程參考如下:
實現思路
- 載入更多資料使用到 RecyclerView 載入多種佈局,根據 ViewType 判斷載入資料 Item 還是載入 FooterItem ;
- 通過執行緒模擬載入資料;
- 為 RecyclerView 新增 addOnScrollListener 事件來監聽使用者的滑動操作;
- 根據使用者滑動狀態以及具體情況開始載入資料
- 通知資料更新;
如何獲得 firstVisibleItemPosition
為了能夠在資料載入中動態判斷什麼時候載入資料,需要知道螢幕上顯示的第一個可見的 Item 的位置,當然了這裡使用的是佈局管理器是 LinearLayoutManager ,這樣查詢螢幕上第一個可見的 Item 就顯得容易多了,下面介紹一些 LinearLayoutManager 的四個方法:
findFirstVisibleItemPosition()
獲得螢幕上第一個可見 Item 的 position,只要該 Item 有一部分可見,那麼返回的 position 就是該Item 的 position。
findFirstCompletelyVisibleItemPosition()
獲得螢幕上第一個完整可見的 Item 的 position,只要該 Item 有一部分不可見,那麼返回的 position 就是該 Item 對應的下一個能顯示完整的 Item 的position。
findLastVisibleItemPosition()
獲得螢幕上最後一個可見 Item 的 position,只要該 Item 有一部分可見,那麼返回的 position 就是該Item 的 position。
findLastCompletelyVisibleItemPosition()
獲得螢幕上最後一個完整可見的 Item 的 position,只要該 Item 有一部分不可見,那麼返回的 position 就是該 Item 對應的上一個能顯示完整的 Item 的position。
準備資料
/**
* 初始化資料
* @return
*/
public void initData(){
for (int i=0;i<30;i++){
arrayList.add("第"+i+"條資料");
}
}
/**
* 執行緒模擬載入資料
*/
class LoadDataThread extends Thread{
@Override
public void run() {
initData();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//通知主執行緒更新資料
Message message = handler.obtainMessage();
message.what = UPDATE_DATA;
message.obj = arrayList;
handler.sendMessage(message);
}
}
程式碼參考
主佈局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.manu.mrecyclerview.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
Item佈局
/**item.xml**/
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp">
<TextView
android:id="@+id/tv_recycle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="data"
android:background="#cac3c3"
android:padding="10dp"
android:textSize="20sp"/>
</LinearLayout>
/**item_footer.xml**/
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal">
<ProgressBar
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/progressBar" />
<TextView
android:text="正在努力載入中,請稍後..."
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textView" />
</LinearLayout>
Adapter
這裡使用了 RecyclerView 根據不同的 ViewType 載入多種佈局的用法,使用時根據不同的佈局建立不同的 ViewHolder , 然後根據不同的 Viewholder 為對應的 Item 新增資料,注意 getItemViewType() 方法的用法,Adapter 程式碼參考如下:
/**
* Created by jzman on 2017/6/04
* RecycleView的Adapter
*/
public class RvAdapter1 extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements
View.OnClickListener{
private static final int ITEM_FOOTER = 0x1;
private static final int ITEM_DATA = 0x2;
private Context mContext;
private RecyclerView recyclerView;
private ArrayList<String> mList;
public RvAdapter1() {}
public RvAdapter1(Context mContext, ArrayList<String> mList) {
this.mContext = mContext;
this.mList = mList;
}
public void setmList(ArrayList<String> mList) {
this.mList = mList;
}
/**
* 用於建立ViewHolder
* @param parent
* @param viewTypez
* @return
*/
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view ;
RecyclerView.ViewHolder vh = null;
switch (viewType){
case ITEM_DATA:
view = LayoutInflater.from(mContext).inflate(R.layout.item,null);
view.setOnClickListener(this);
vh = new DataViewHolder(view);
//使用程式碼設定寬高(xml佈局設定無效時)
view.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
break;
case ITEM_FOOTER:
view = LayoutInflater.from(mContext).inflate(R.layout.item_footer,null);
//使用程式碼設定寬高(xml佈局設定無效時)
vh = new FooterViewHolder(view);
view.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
break;
}
return vh;
}
/**
* 獲取Item的View型別
* @param position
* @return
*/
@Override
public int getItemViewType(int position) {
//根據 Item 的 position 返回不同的 Viewtype
if (position == (getItemCount())-1){
return ITEM_FOOTER;
}else{
return ITEM_DATA;
}
}
/**
* 繫結資料
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof DataViewHolder){
DataViewHolder dataViewHolder = (DataViewHolder) holder;
dataViewHolder.tv_data.setText(mList.get(position));
}else if (holder instanceof FooterViewHolder){
}
}
/**
* 選項總數
* @return
*/
@Override
public int getItemCount() {
return mList.size()+1;
}
@Override
public void onClick(View view) {
//根據RecyclerView獲得當前View的位置
int position = recyclerView.getChildAdapterPosition(view);
//程式執行到此,會去執行具體實現的onItemClick()方法
if (onItemClickListener!=null){
onItemClickListener.onItemClick(recyclerView,view,position,mList.get(position));
}
}
/**
* 建立ViewHolder
*/
public static class DataViewHolder extends RecyclerView.ViewHolder{
TextView tv_data;
public DataViewHolder(View itemView) {
super(itemView);
tv_data = (TextView) itemView.findViewById(R.id.tv_recycle);
}
}
/**
* 建立footer的ViewHolder
*/
public static class FooterViewHolder extends RecyclerView.ViewHolder{
public FooterViewHolder(View itemView) {
super(itemView);
}
}
private OnItemClickListener onItemClickListener;
public void setOnItemClickListener(OnItemClickListener onItemClickListener){
this.onItemClickListener = onItemClickListener;
}
/**
* 定義RecyclerView選項單擊事件的回撥介面
*/
public interface OnItemClickListener{
//引數(父元件,當前單擊的View,單擊的View的位置,資料)
void onItemClick(RecyclerView parent,View view, int position, String data);
}
/**
* 將RecycleView附加到Adapter上
*/
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
this.recyclerView= recyclerView;
}
/**
* 將RecycleView從Adapter解除
*/
@Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
this.recyclerView = null;
}
}
MainActivity
這裡主要注意 rv.addOnScrollListener(new OnScrollListener() …裡面的具體實現,MainActivity 程式碼參考如下:
/**
* Created by jzman on 2017/6/04 0013.
*/
public class MainActivity extends AppCompatActivity {
private static final int UPDATE_DATA = 0x3;
private RecyclerView rv;
RvAdapter1 adapter;
private ArrayList<String> arrayList = new ArrayList<>();
//載入更多資料時最後一項的索引
private int lastLoadDataItemPosition;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rv = (RecyclerView) findViewById(R.id.rv);
//設定佈局管理器
rv.setLayoutManager(new LinearLayoutManager(this));//線性
// rv.setLayoutManager(new GridLayoutManager(this,4));//線性
// rv.setLayoutManager(new StaggeredGridLayoutManager(4,StaggeredGridLayoutManager.VERTICAL));//線性
initData();
adapter = new RvAdapter1(this,arrayList);
adapter.setOnItemClickListener(new RvAdapter1.OnItemClickListener() {
@Override
public void onItemClick(RecyclerView parent, View view, int position, String data) {
Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
}
});
rv.addOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == SCROLL_STATE_IDLE &&
lastLoadDataItemPosition == adapter.getItemCount()){
new LoadDataThread().start();
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager){
LinearLayoutManager manager = (LinearLayoutManager) layoutManager;
int firstVisibleItem = manager.findFirstVisibleItemPosition();
int l = manager.findLastCompletelyVisibleItemPosition();
lastLoadDataItemPosition = firstVisibleItem+(l-firstVisibleItem)+1;
}
}
});
rv.setAdapter(adapter);
}
/**
* 初始化資料
* @return
*/
public void initData(){
for (int i=0;i<25;i++){
arrayList.add("第"+i+"條資料");
}
}
/**
* 執行緒模擬載入資料
*/
class LoadDataThread extends Thread{
@Override
public void run() {
initData();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message = handler.obtainMessage();
message.what = UPDATE_DATA;
message.obj = arrayList;
handler.sendMessage(message);
}
}
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case UPDATE_DATA:
arrayList = (ArrayList<String>) msg.obj;
adapter.setmList(arrayList);
adapter.notifyDataSetChanged();
break;
}
}
};
}
測試效果
可關注微信公眾號:jzman-blog 獲取最新文章更新,一起交流學習!