Material Design RecyclerView新增頭部底部佈局(五)
阿新 • • 發佈:2019-01-26
前言:相信很多人都知道,現在的日常開發中,已經逐漸由RecyclerView代替ListView、GridView,但相對於後者來說,RecyclerView存在著一些缺點,比如這篇文章要講的,RecyclerView沒有新增頭部佈局和底部佈局的方法。
想到這裡,我就想著那listview裡面是怎麼實現的呢?是不是可以去模仿Listview也寫一個自己的RecyclerView,話不多說,直接開始
ListView原始碼分析:
//新增存放頭部View和底部view的集合 private ArrayList<FixedViewInfo> mHeaderViewInfos = Lists.newArrayList(); private ArrayList<FixedViewInfo> mFooterViewInfos = Lists.newArrayList(); //新增頭部 public void addHeaderView(View v, Object data, boolean isSelectable) { //儲存頭部資訊 final FixedViewInfo info = new FixedViewInfo(); info.view = v; info.data = data; info.isSelectable = isSelectable; //新增資訊到頭部集合 mHeaderViewInfos.add(info); mAreAllItemsSelectable &= isSelectable; //如果mAdapter不為空,這個if的作用個人理解應該是在setAdapter後,使用者假如再次新增頭部佈局, 會將原先已經經歷過一次包裝的HeaderViewListAdapter再一次包裝 if (mAdapter != null) { if (!(mAdapter instanceof HeaderViewListAdapter)) { //如果此adapter沒有被包裝過,包裝成具有HeaderView的adapter mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter); } if (mDataSetObserver != null) { mDataSetObserver.onChanged(); } } } public void addFooterView(View v, Object data, boolean isSelectable) { //儲存底部資訊 final FixedViewInfo info = new FixedViewInfo(); info.view = v; info.data = data; info.isSelectable = isSelectable; //新增資訊到底部集合 mFooterViewInfos.add(info); mAreAllItemsSelectable &= isSelectable; //如果mAdapter不為空,這個if的作用個人理解應該是在setAdapter後,使用者假如再次新增底部佈局, 會將原先已經經歷過一次包裝的HeaderViewListAdapter再一次包裝 if (mAdapter != null) { if (!(mAdapter instanceof HeaderViewListAdapter)) { //如果此adapter沒有被包裝過,包裝成具有FooterView的adapter mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter); } if (mDataSetObserver != null) { mDataSetObserver.onChanged(); } } } @Override public void setAdapter(ListAdapter adapter) { if (mAdapter != null && mDataSetObserver != null) { mAdapter.unregisterDataSetObserver(mDataSetObserver); } resetList(); mRecycler.clear(); //如果頭部集合或者底部集合不為空,則將Activity編寫的adapter帶入到HeadViewListAdapter進行包裝,否則還是用原來的adapter if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) { mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter); } else { mAdapter = adapter; } mOldSelectedPosition = INVALID_POSITION; mOldSelectedRowId = INVALID_ROW_ID; // AbsListView#setAdapter will update choice mode states. super.setAdapter(adapter); if (mAdapter != null) { mAreAllItemsSelectable = mAdapter.areAllItemsEnabled(); mOldItemCount = mItemCount; mItemCount = mAdapter.getCount(); checkFocus(); mDataSetObserver = new AdapterDataSetObserver(); mAdapter.registerDataSetObserver(mDataSetObserver); mRecycler.setViewTypeCount(mAdapter.getViewTypeCount()); int position; if (mStackFromBottom) { position = lookForSelectablePosition(mItemCount - 1, false); } else { position = lookForSelectablePosition(0, true); } setSelectedPositionInt(position); setNextSelectedPositionInt(position); if (mItemCount == 0) { // Nothing selected checkSelectionChanged(); } } else { mAreAllItemsSelectable = true; checkFocus(); // Nothing selected checkSelectionChanged(); } requestLayout(); } HeaderViewListAdapter: //得到主體item、頭部item、底部item總和 public int getCount() { if (mAdapter != null) { return getFootersCount() + getHeadersCount() + mAdapter.getCount(); } else { return getFootersCount() + getHeadersCount(); } } //根據不同條件返回主體、頭部、底部View public View getView(int position, View convertView, ViewGroup parent) { // Header (negative positions will throw an IndexOutOfBoundsException) int numHeaders = getHeadersCount(); if (position < numHeaders) { return mHeaderViewInfos.get(position).view; } // Adapter final int adjPosition = position - numHeaders; int adapterCount = 0; if (mAdapter != null) { adapterCount = mAdapter.getCount(); if (adjPosition < adapterCount) { return mAdapter.getView(adjPosition, convertView, parent); } } // Footer (off-limits positions will throw an IndexOutOfBoundsException) return mFooterViewInfos.get(adjPosition - adapterCount).view; }
以上程式碼運用了強大的裝飾者模式,裝飾者模式實現的是從一個物件外部給物件新增功能,
相當於改變了物件的外觀,裝飾過的物件,從外部系統來看已經不再是原來的物件,而是經過一系列裝飾器裝飾過的物件。
這裡就是將自己的寫的Adapter轉換成了內部的HeadViewListAdapter。
這裡我們知道了listview如何實現新增頭部和底部佈局的現在我們就來模仿listview做一個新增頭部和底部佈局的Recyclerview。
WrapRecyclerView.java:
public class WrapRecyclerView extends RecyclerView{ private ArrayList<View> mHeaderViewInfos = new ArrayList<View>(); private ArrayList<View> mFooterViewInfos = new ArrayList<View>(); private Adapter mAdapter; public WrapRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); } public void addHeaderView(View v) { mHeaderViewInfos.add(v); // Wrap the adapter if it wasn't already wrapped. if (mAdapter != null) { if (!(mAdapter instanceof HeaderViewRecyclerAdapter)) { mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter); } } } public void addFooterView(View v) { mFooterViewInfos.add(v); // Wrap the adapter if it wasn't already wrapped. if (mAdapter != null) { if (!(mAdapter instanceof HeaderViewRecyclerAdapter)) { mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter); } } } @Override public void setAdapter(Adapter adapter) { if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) { mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, adapter); } else { mAdapter = adapter; } super.setAdapter(mAdapter); } }
HeaderViewRecyclerAdapter.java:
public class HeaderViewRecyclerAdapter extends RecyclerView.Adapter { private RecyclerView.Adapter mAdapter; ArrayList<View> mHeaderViewInfos; ArrayList<View> mFooterViewInfos; public HeaderViewRecyclerAdapter(ArrayList<View> headerViewInfos, ArrayList<View> footerViewInfos, RecyclerView.Adapter adapter) { mAdapter = adapter; if (headerViewInfos == null) { mHeaderViewInfos = new ArrayList<View>(); } else { mHeaderViewInfos = headerViewInfos; } if (footerViewInfos == null) { mFooterViewInfos = new ArrayList<View>(); } else { mFooterViewInfos = footerViewInfos; } } //判讀當前條目是什麼型別的--決定渲染什麼檢視 @Override public int getItemViewType(int position) { int numHeaders = getHeadersCount(); if (position < numHeaders) { //是頭部 return RecyclerView.INVALID_TYPE; } //正常條目部分 final int adjPosition = position - numHeaders; int adapterCount = 0; if (mAdapter != null) { adapterCount = mAdapter.getItemCount(); if (adjPosition < adapterCount) { return mAdapter.getItemViewType(adjPosition); } } //footer部分 return RecyclerView.INVALID_TYPE - 1; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { //header if(viewType == RecyclerView.INVALID_TYPE){//頭部 return new HeadViewHolder(mHeaderViewInfos.get(0)); }else if(viewType == RecyclerView.INVALID_TYPE-1){//尾部 return new HeadViewHolder(mFooterViewInfos.get(0)); } return mAdapter.onCreateViewHolder(parent,viewType); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { //也要劃分三個區域 int numHeaders = getHeadersCount(); if (position < numHeaders) { //是頭部 return; } //adapter body final int adjPosition = position - numHeaders; int adapterCount = 0; if (mAdapter != null) { adapterCount = mAdapter.getItemCount(); if (adjPosition < adapterCount) { mAdapter.onBindViewHolder(holder,adjPosition); return ; } } //footer部分 } @Override public int getItemCount() { if (mAdapter != null) { return getFootersCount() + getHeadersCount() + mAdapter.getItemCount(); } else { return getFootersCount() + getHeadersCount(); } } public int getHeadersCount() { return mHeaderViewInfos.size(); } public int getFootersCount() { return mFooterViewInfos.size(); } private static class HeadViewHolder extends RecyclerView.ViewHolder{ public HeadViewHolder(View itemView) { super(itemView); } } }
MyAdapter.java:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private List<String> list;
public MyAdapter(List<String> list) {
this.list = list;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view = layoutInflater.inflate(R.layout.listitem, parent, false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.tv.setText(list.get(position));
}
@Override
public int getItemCount() {
return list.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
public TextView tv;
public ViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv);
}
}
}
MainActivity.java:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WrapRecyclerView recyclerView = (WrapRecyclerView) findViewById(R.id.recyclerview);
//View headerView = View.inflate(this,resource,root);
TextView headerView = new TextView(this);
//TextView tv = headerView.findViewById(id);
ActionBar.LayoutParams params = new ActionBar.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
headerView.setLayoutParams(params);
headerView.setText("top");
recyclerView.addHeaderView(headerView);
TextView footview = new TextView(this);
footview.setLayoutParams(params);
footview.setText("bottom");
recyclerView.addFooterView(footview);
List<String> list = new ArrayList<>();
for (int i = 0; i < 30; i++) {
list.add("item"+i);
}
MyAdapter adapter = new MyAdapter(list);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
}
}
這裡相關的佈局檔案也很簡單,這裡就不貼程式碼了。
總結完畢。
不喜勿噴 謝謝!