天天看點

Recycleview之更簡便添加頭部尾部概述ListView添加頭部尾部分析Recycleview添加頭部尾部使用與優化源碼

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的寫法