Recycleview之更簡便添加頭部尾部
- 概述
- ListView添加頭部尾部分析
-
- ListView源碼分析
- ListView總結
- Recycleview添加頭部尾部
-
- 1.adpter外部添加一個帶頭尾的adpter
- 使用與優化
- 源碼
概述
之前寫過一篇關于通用的adapter的寫法 ,在此基礎上有添加了頭部和尾部功能,因為ListView是封裝了添加頭部和尾部功能的,是以我們本着學習的态度研究研究
ListView添加頭部尾部分析
ListView源碼分析
首先進入到ListView.addHeaderView 方法,方法有兩個 但是最終都是調用了 public void addHeaderView(View v, Object data, boolean isSelectable) 這個方法 我們看看在這個方法中做了什麼
public void addHeaderView(View v) {
addHeaderView(v, null, true);
}
public void addHeaderView(View v, Object data, boolean isSelectable) {
if (v.getParent() != null && v.getParent() != this) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "The specified child already has a parent. "
+ "You must call removeView() on the child's parent first.");
}
}
//建立 FixedViewInfo類 并初始化
final FixedViewInfo info = new FixedViewInfo();
info.view = v;
info.data = data;
info.isSelectable = isSelectable;
//在緩存中添加FixedViewInfo
mHeaderViewInfos.add(info);
mAreAllItemsSelectable &= isSelectable;
// Wrap the adapter if it wasn't already wrapped.
if (mAdapter != null) {
//判斷不是HeaderViewListAdapter 建立一個HeaderViewListAdapter
//并将adpter傳進去
if (!(mAdapter instanceof HeaderViewListAdapter)) {
wrapHeaderListAdapterInternal();
}
// In the case of re-adding a header view, or adding one later on,
// we need to notify the observer.
//英文介紹了 通知觀察者
if (mDataSetObserver != null) {
mDataSetObserver.onChanged();
}
}
}
/** @hide */
protected void wrapHeaderListAdapterInternal() {
mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter);
}
接下來我們進入到HeaderViewListAdapter 看看有什麼乾坤
public HeaderViewListAdapter(ArrayList<ListView.FixedViewInfo> headerViewInfos,
ArrayList<ListView.FixedViewInfo> footerViewInfos,
ListAdapter adapter) {
mAdapter = adapter;
mIsFilterable = adapter instanceof Filterable;
//判斷頭部是否為空
if (headerViewInfos == null) {
mHeaderViewInfos = EMPTY_INFO_LIST;
} else {
mHeaderViewInfos = headerViewInfos;
}
//判斷尾部是否為空
if (footerViewInfos == null) {
mFooterViewInfos = EMPTY_INFO_LIST;
} else {
mFooterViewInfos = footerViewInfos;
}
mAreAllFixedViewsSelectable =
areAllListInfosSelectable(mHeaderViewInfos)
&& areAllListInfosSelectable(mFooterViewInfos);
}
public View getView(int position, View convertView, ViewGroup parent) {
// Header (negative positions will throw an IndexOutOfBoundsException)
//判斷頭部個數 傳回頭部view
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return mHeaderViewInfos.get(position).view;
}
// Adapter
//傳回adpter的資料
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)
//傳回尾部view
return mFooterViewInfos.get(adjPosition - adapterCount).view;
}
ListView總結
很簡單 ,總體來說就是建立了一個新的adpter ,将老的adpter外部包裹一個adpter。在新的adpter中将頭部尾部加入到list集合中在擷取view的時候根據不同位置加載相應的布局,getCount 和getItem 都類似就不在這裡寫了,getCount就是頭部個數+尾部個數+傳過來的adpter的資料個數。 getItem就是根據不同位置傳回不同的資料
Recycleview添加頭部尾部
1.adpter外部添加一個帶頭尾的adpter
首先我們用SparseArray來代替HashMap實作一個頭尾的緩存數組,同時初始化頭部和尾部的Key并接收傳進來的Adapter
初始化完成
private RecyclerView.Adapter mAdapter;
private SparseArray<View> mHeaders ,mFooters;
private static int BASE_HEADER_KEY = 1000000;
private static int BASE_FOOTER_KEY = 2000000;
public HeaderFooterAdapter(RecyclerView.Adapter adapter) {
this.mAdapter = adapter;
mHeaders = new SparseArray<>();
mFooters = new SparseArray<>();
}
首先多布局的adpter我們必須實作getItemViewType方法并傳入不同的ViewType已加載不同布局,學習ListView的寫法我們這麼做:
@Override
public int getItemViewType(int position) {
int numHeaders = mHeaders.size();
if (position < numHeaders) {
//頭部傳回 緩存的Key
return mHeaders.keyAt(position);
}
// Adapter 資料傳回傳入adpter對應的ViewType
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getItemCount();
if (adjPosition < adapterCount) {
Log.e("TAG", "getItemViewType: -->"+adjPosition +"....."+adapterCount );
return mAdapter.getItemViewType(adjPosition);
}
}
//尾部傳回 緩存的Key
return mFooters.keyAt(adjPosition-adapterCount);
}
接下來 我們實作 onCreateViewHolder
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//搜尋查到傳回>0
if (mHeaders.indexOfKey(viewType)>=0){
//添加頭部尾部的ViewHolder
return createFooterHeaderViewHolder(mHeaders.get(viewType));
}else if (mFooters.indexOfKey(viewType)>=0){
return createFooterHeaderViewHolder(mFooters.get(viewType));
}
//傳回傳入adpter的onCreateViewHolder
return mAdapter.onCreateViewHolder(parent,viewType);
}
private RecyclerView.ViewHolder createFooterHeaderViewHolder(View view) {
return new RecyclerView.ViewHolder(view) {
};
}
接下來是onBindViewHolder
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
int numHeaders = mHeaders.size();
//頭部的你進行指派操作
if (position < numHeaders) {
return ;
}
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getItemCount();
if (adjPosition < adapterCount) {
mAdapter.onBindViewHolder(holder,adjPosition);
}
}
}
其他部分代碼
@Override
public int getItemCount() {
//傳回個數
return mAdapter.getItemCount()+mFooters.size()+mHeaders.size();
}
public void addHeaderView(View view){
//添加頭部
if (mHeaders.indexOfValue(view)==-1){
mHeaders.put(BASE_HEADER_KEY++,view);
}
}
public void addFooterView(View view){
//添加尾部
if (mFooters.indexOfValue(view)==-1){
mFooters.put(BASE_FOOTER_KEY++,view);
}
}
public void removeHeaderView(View view){
//删除頭部
if (mHeaders.indexOfValue(view)>=0){
mHeaders.removeAt(mHeaders.indexOfValue(view));
notifyDataSetChanged();
}
}
//删除尾部
public void removeFooterView(View view){
if (mFooters.indexOfValue(view)>=0){
mFooters.removeAt(mFooters.indexOfValue(view));
notifyDataSetChanged();
}
}
使用與優化
MixLayoutAdapter mixLayoutAdapter = new MixLayoutAdapter(this, strings);
HeaderFooterAdapter headerFooterAdapter = new HeaderFooterAdapter(mixLayoutAdapter);
View view = LayoutInflater.from(this).inflate(R.layout.layout_header,recyclerView,false);
headerFooterAdapter.addFooterView(view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(headerFooterAdapter);
View view = LayoutInflater.from(this).inflate(R.layout.layout_header,recyclerView,false);
headerFooterAdapter.addHeaderView(view);
這樣使用的話太複雜,需要建立和修改的東西太多是以我仿照ListView建立了一個HeaderRecycleView
讓他繼承RecyclerView。
**
* @author li
* 版本:1.0
* 建立日期:2020/6/13 17
* 描述:
*/
public class HeaderRecycleView extends RecyclerView {
private HeaderFooterAdapter mAdapter;
private AdapterDataObserver mAdapterDataObserver = new AdapterDataObserver() {
@Override
public void onChanged() {
mAdapter.notifyDataSetChanged();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
mAdapter.notifyItemRangeChanged(positionStart, itemCount, null);
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
mAdapter.notifyItemRangeChanged(positionStart,itemCount,payload);
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
mAdapter.notifyItemRangeInserted(positionStart,itemCount);
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
mAdapter.notifyItemRangeRemoved(positionStart,itemCount);
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
mAdapter.notifyItemMoved(fromPosition,toPosition);
}
};
public HeaderRecycleView(@NonNull Context context) {
super(context);
}
public HeaderRecycleView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public HeaderRecycleView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//重要代碼
@Override
public void setAdapter(@Nullable Adapter adapter) {
if (adapter instanceof HeaderFooterAdapter){
mAdapter = (HeaderFooterAdapter) adapter;
}else {
mAdapter = new HeaderFooterAdapter(adapter);
//監控資料變化進行删除相關的操作時使用
mAdapter.registerAdapterDataObserver(mAdapterDataObserver);
}
super.setAdapter(adapter);
}
public void addHeaderView(View view){
if (mAdapter!=null){
mAdapter.addHeaderView(view);
}
}
public void addFooterView(View view){
if (mAdapter!=null){
mAdapter.addFooterView(view);
}
}
public void removeHeaderView(View view){
if (mAdapter!=null){
mAdapter.addHeaderView(view);
}
}
public void removeFooterView(View view){
if (mAdapter!=null){
mAdapter.removeFooterView(view);
}
}
}
現在使用
MixLayoutAdapter mixLayoutAdapter = new MixLayoutAdapter(this, strings);
mixLayoutAdapter.SetOnClickListener(new AdapterOnItemClick<String>() {
@Override
public void click(View view, String s, int type, int pos) {
Log.e("TAG", "click:-------> " + s + "...." + type + "......" + pos);
}
});
mixLayoutAdapter.SetOnLongClickListener(new AdapterOnLongItemClick<String>() {
@Override
public void click(View view, String s, int type, int pos) {
Log.e("長點選TAG", "click:-------> " + s + "...." + type + "......" + pos);
}
});
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(mixLayoutAdapter);
View view = LayoutInflater.from(this).inflate(R.layout.layout_header,recyclerView,false);
recyclerView.addHeaderView(view);
recyclerView.addFooterView(view);
源碼
源碼我和之前寫的Adpter放在一起了可以去檢視,如果有更好的方法和建議請告知我,不斷前行。
通用的adapter的寫法