裝飾設(shè)計(jì)模式 - RecyclerView添加頭部和底部

定義:

裝飾設(shè)計(jì)模式也稱包裝設(shè)計(jì)模式,用來(lái)動(dòng)態(tài)的擴(kuò)展對(duì)象的功能,也是繼承關(guān)系的的一種替代方案之一。
說(shuō)個(gè)大白話就是,在不使用的繼承的方式下,采用裝飾設(shè)計(jì)模式可以擴(kuò)展一個(gè)對(duì)象的功能,可以使一個(gè)對(duì)象變得越來(lái)越強(qiáng)大。

我們首先看下效果圖
效果圖.gif
我們都知道listview是可以添加頭部和尾部的,我們大概看下,具體的可以自己去看下,源碼并不難。
public void setAdapter(ListAdapter adapter) {
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }

        resetList();
        mRecycler.clear();

        if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
         //實(shí)際上呢,listview是將adapter添加了一個(gè)包裹類
            mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
        } else {
            mAdapter = adapter;
        }
    //代碼省略
}

我們可以模仿listview進(jìn)行adapter的封裝

首先WrapRecyclerAdapter類是繼承于RecylerView的adapter,參數(shù)如下

   private final static String TAG = "WrapRecyclerAdapter";
    // 用來(lái)存放底部和頭部View的集合  比Map要高效一些
    private SparseArray<View> mHeaderViews;
    private SparseArray<View> mFooterViews;

    // 基本的頭部類型開始位置  用于viewType
    private static int BASE_ITEM_TYPE_HEADER = 10000000;
    // 基本的底部類型開始位置  用于viewType
    private static int BASE_ITEM_TYPE_FOOTER = 20000000;
    // 列表的Adapter
    private RecyclerView.Adapter mAdapter;
 //通過(guò)構(gòu)造函數(shù)的方法
  public WrapRecyclerAdapter(RecyclerView.Adapter adapter) {
        this.mAdapter = adapter;
        mHeaderViews = new SparseArray<>();
        mFooterViews = new SparseArray<>();
    }

我們首先定義四個(gè)方法,添加頭部,尾部,刪除頭部,尾部

  /**
     * 添加頭部
     */
    public void addHeaderView(View view) {
        int position = mHeaderViews.indexOfValue(view);
        if (position < 0) {
            mHeaderViews.put(BASE_ITEM_TYPE_HEADER++, view);
        }
        notifyDataSetChanged();
    }

    /**
     * 添加底部
     */
    public void addFooterView(View view) {
        int position = mFooterViews.indexOfValue(view);
        if (position < 0) {
            mFooterViews.put(BASE_ITEM_TYPE_FOOTER++, view);
        }
        notifyDataSetChanged();
    }

    /**
     * 移除頭部
     */
    public void removeHeaderView(View view) {
        int index = mHeaderViews.indexOfValue(view);
        if (index < 0) return;
        mHeaderViews.removeAt(index);
        notifyDataSetChanged();
    }

    /**
     * 移除底部
     */
    public void removeFooterView(View view) {
        int index = mFooterViews.indexOfValue(view);
        if (index < 0) return;
        mFooterViews.removeAt(index);
        notifyDataSetChanged();
    }

然后我們看下我們復(fù)寫的第一個(gè)方法 getItemCount()很簡(jiǎn)單: 底部條數(shù) + 頭部條數(shù) + Adapter的條數(shù)

    @Override
    public int getItemCount() {
        // 條數(shù)三者相加 = 底部條數(shù) + 頭部條數(shù) + Adapter的條數(shù)
        return mAdapter.getItemCount() + mHeaderViews.size() + mFooterViews.size();
    }

然后看下我們復(fù)寫的第二個(gè)方法onCreateViewHolder()方法

 @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        // viewType 可能就是 SparseArray 的key
        if (isHeaderViewType(viewType)) {
            View headerView = mHeaderViews.get(viewType);
            return createHeaderFooterViewHolder(headerView);
        }

        if (isFooterViewType(viewType)) {
            View footerView = mFooterViews.get(viewType);
            return createHeaderFooterViewHolder(footerView);
        }
        return mAdapter.onCreateViewHolder(parent, viewType);
    }

判斷是不是頭部或者底部類型并創(chuàng)建ViewHolder

/**
     * 是不是底部類型
     */
    private boolean isFooterViewType(int viewType) {
        int position = mFooterViews.indexOfKey(viewType);
        return position >= 0;
    }

    /**
     * 創(chuàng)建頭部或者底部的ViewHolder
     */
    private RecyclerView.ViewHolder createHeaderFooterViewHolder(View view) {
        return new RecyclerView.ViewHolder(view) {

        };
    }

    /**
     * 是不是頭部類型
     */
    private boolean isHeaderViewType(int viewType) {
        int position = mHeaderViews.indexOfKey(viewType);
        return position >= 0;
    }

注意這里viewType不是position所以我們一定要復(fù)寫getItemViewType將返回的值賦值給viewType

@Override
   public int getItemViewType(int position) {
       if (isHeaderPosition(position)) {
           // 直接返回position位置的key
           return mHeaderViews.keyAt(position);
       }
       if (isFooterPosition(position)) {
           // 直接返回position位置的key
           position = position - mHeaderViews.size() - mAdapter.getItemCount();
           return mFooterViews.keyAt(position);
       }
       // 返回列表Adapter的getItemViewType
       position = position - mHeaderViews.size();
       return mAdapter.getItemViewType(position);
   }

看最后一個(gè)復(fù)寫的方法onBindViewHolder()

@Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (isHeaderPosition(position) || isFooterPosition(position)) {
            return;
        }
        // 計(jì)算一下位置
        final int adapterPosition = position - mHeaderViews.size();
        mAdapter.onBindViewHolder(holder, adapterPosition);

        // 設(shè)置點(diǎn)擊和長(zhǎng)按事件
        if (mItemClickListener != null) {
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mItemClickListener.onItemClick(adapterPosition);
                }
            });
        }
        if (mLongClickListener != null) {
            holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    return mLongClickListener.onLongClick(adapterPosition);
                }
            });
        }
    }

/判斷是不是頭部或者底部的位置

  /**
     * 是不是底部位置
     */
    private boolean isFooterPosition(int position) {
        return position >= (mHeaderViews.size() + mAdapter.getItemCount());
    }

    /**
     * 是不是頭部位置
     */
    private boolean isHeaderPosition(int position) {
        return position < mHeaderViews.size();
    }

好了,這時(shí)候我們的裝飾的adapter已經(jīng)寫好了,接下來(lái)我們寫下RecylerView的包裹類,不然我們這樣使用

   RecyclerAdapter mRealAdapter = new RecyclerAdapter();
        WrapRecyclerAdapter wrapRecyclerAdapter = new WrapRecyclerAdapter(mRealAdapter);
        // setAdapter
        mRecyclerView.setAdapter(wrapRecyclerAdapter);
    //添加頭尾部

這這樣使用感覺(jué)很尷尬啊

WrapRecyclerView繼承于RecyclerView內(nèi)部

  // 包裹了一層的頭部底部Adapter
    private WrapRecyclerAdapter mWrapRecyclerAdapter;
    // 這個(gè)是列表數(shù)據(jù)的Adapter
    private Adapter mAdapter;

    // 增加一些通用功能
    // 空列表數(shù)據(jù)應(yīng)該顯示的空View
    // 正在加載數(shù)據(jù)頁(yè)面,也就是正在獲取后臺(tái)接口頁(yè)面
    private View mEmptyView, mLoadingView;
public WrapRecyclerView(Context context) {
        super(context);
    }

    public WrapRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public WrapRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

還是先看我們添加和刪除的的四個(gè)方法

  // 添加頭部
    public void addHeaderView(View view) {
        // 如果沒(méi)有Adapter那么就不添加,也可以選擇拋異常提示
        // 讓他必須先設(shè)置Adapter然后才能添加,這里是仿照ListView的處理方式
        if (mWrapRecyclerAdapter != null) {
            mWrapRecyclerAdapter.addHeaderView(view);
        }
    }

    // 添加底部
    public void addFooterView(View view) {
        if (mWrapRecyclerAdapter != null) {
            mWrapRecyclerAdapter.addFooterView(view);
        }
    }

    // 移除頭部
    public void removeHeaderView(View view) {
        if (mWrapRecyclerAdapter != null) {
            mWrapRecyclerAdapter.removeHeaderView(view);
        }
    }

    // 移除底部
    public void removeFooterView(View view) {
        if (mWrapRecyclerAdapter != null) {
            mWrapRecyclerAdapter.removeFooterView(view);
        }
    }

復(fù)寫setAdapter的方法

  @Override
    public void setAdapter(Adapter adapter) {
        // 為了防止多次設(shè)置Adapter
        if (mAdapter != null) {
            mAdapter.unregisterAdapterDataObserver(mDataObserver);
            mAdapter = null;
        }

        this.mAdapter = adapter;

        if (adapter instanceof WrapRecyclerAdapter) {
            mWrapRecyclerAdapter = (WrapRecyclerAdapter) adapter;
        } else {
            mWrapRecyclerAdapter = new WrapRecyclerAdapter(adapter);
        }

        super.setAdapter(mWrapRecyclerAdapter);

        // 注冊(cè)一個(gè)觀察者
        mAdapter.registerAdapterDataObserver(mDataObserver);

        // 解決GridLayout添加頭部和底部也要占據(jù)一行
        mWrapRecyclerAdapter.adjustSpanSize(this);

        // 加載數(shù)據(jù)頁(yè)面
        if (mLoadingView != null && mLoadingView.getVisibility() == View.VISIBLE) {
            mLoadingView.setVisibility(View.GONE);
        }

        if (mItemClickListener != null) {
            mWrapRecyclerAdapter.setOnItemClickListener(mItemClickListener);
        }

        if (mLongClickListener != null) {
            mWrapRecyclerAdapter.setOnLongClickListener(mLongClickListener);
        }
    }

mDataObserver注冊(cè)器的使用呢

private AdapterDataObserver mDataObserver = new AdapterDataObserver() {
        @Override
        public void onChanged() {
            if (mAdapter == null) {
                return;
            }
            // 觀察者  列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged沒(méi)效果
            if (mWrapRecyclerAdapter != mAdapter) {
                mWrapRecyclerAdapter.notifyDataSetChanged();
            }
            dataChanged();
        }

        @Override
        public void onItemRangeRemoved(int positionStart, int itemCount) {
            if (mAdapter == null) return;
            // 觀察者  列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged沒(méi)效果
            if (mWrapRecyclerAdapter != mAdapter)
                mWrapRecyclerAdapter.notifyItemRemoved(positionStart);
            dataChanged();
        }

        @Override
        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
            if (mAdapter == null) {
                return;
            }
            // 觀察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemMoved沒(méi)效果
            if (mWrapRecyclerAdapter != mAdapter) {
                mWrapRecyclerAdapter.notifyItemMoved(fromPosition, toPosition);
            }
            dataChanged();
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount) {
            if (mAdapter == null) {
                return;
            }
            // 觀察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemChanged沒(méi)效果
            if (mWrapRecyclerAdapter != mAdapter) {
                mWrapRecyclerAdapter.notifyItemChanged(positionStart);
            }
            dataChanged();
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
            if (mAdapter == null) {
                return;
            }
            // 觀察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemChanged沒(méi)效果
            if (mWrapRecyclerAdapter != mAdapter) {
                mWrapRecyclerAdapter.notifyItemChanged(positionStart, payload);
            }
            dataChanged();
        }

        @Override
        public void onItemRangeInserted(int positionStart, int itemCount) {
            if (mAdapter == null) {
                return;
            }
            // 觀察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemInserted沒(méi)效果
            if (mWrapRecyclerAdapter != mAdapter) {
                mWrapRecyclerAdapter.notifyItemInserted(positionStart);
            }
            dataChanged();
        }
    };

Adapter數(shù)據(jù)改變的方法

  private void dataChanged() {
        if (mAdapter.getItemCount() == 0) {
            // 沒(méi)有數(shù)據(jù)
            if (mEmptyView != null) {
                mEmptyView.setVisibility(VISIBLE);
            } else {
                mEmptyView.setVisibility(GONE);
            }
        }
    }

添加一個(gè)空列表數(shù)據(jù)頁(yè)面和添加一個(gè)正在加載數(shù)據(jù)的頁(yè)面

   /**
     * 添加一個(gè)空列表數(shù)據(jù)頁(yè)面
     */
    public void addEmptyView(View emptyView) {
        this.mEmptyView = emptyView;
    }

    /**
     * 添加一個(gè)正在加載數(shù)據(jù)的頁(yè)面
     */
    public void addLoadingView(View loadingView) {
        this.mLoadingView = loadingView;
        mLoadingView.setVisibility(View.VISIBLE);
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容