RecyclerView封裝Adapter之添加頭部和底部視圖

RecyclerView封裝Adapter之添加頭部和底部視圖

轉載請標明出處:
http://blog.csdn.net/lisdye2/article/details/52674879
本文出自:【Alex_MaHao的博客】
項目中的源碼已經共享到github,有需要者請移步【Alex_MaHao的github】

ListView中已經自帶了添加頭佈局和添加底部佈局的方法,但是在RecyclerView中,卻沒有默認實現,這導致在實現一些特殊佈局中不是那麼的方便,本篇博客將通過封裝Adapter方式添加頭部和底部佈局。

首先看一下實現效果

這裏寫圖片描述

在閱讀本篇博客之前,推薦閱讀上一篇博客 RecyclerView 封裝Adapter之BaseRecyclerAdapter

理論分析

在實現的過程中,我們首先想到的便是複用的問題,如果對於ListViewRecylerView掌握的還可的話,知道在一個條目顯示時候,會複用之前已經隱藏的條目。如果添加頭部或者底部的視圖,他的樣式肯定適合我們普通顯示的條目是不同的,這時候,複用勢必會拋出錯誤,那麼如何解決呢?

細心的同學會發現,在創建RecyclerView.Adapter時,其中的一個方法有些特別public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType),第一個參數是父控件,第二個參數,從參數的命名山可以理解爲視圖的類型,關鍵便就在此,我們可以根據viewType的值不同,構造不同的ViewHoder。 既然能夠獲取,肯定有指定的地方,繼續尋找,發現了下面的方法public int getItemViewType(int position),根據索引值返回對應條目的類型。那麼,一切都捋順了。思路如下:

  • 根據public int getItemViewType(int position),定義頭部,底部,正常條目的常量,根據索引值進行返回。
  • public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType)中,通過viewType的不同值創建不同的ViewHolder.

代碼實現

定義變量,添加頭部佈局和底部佈局

首先,從簡單的開始實現,如下


public abstract class BaseRecycleAdapter<T> extends RecyclerView.Adapter<BaseRecycleAdapter.BaseViewHolder> {

 // 頭部控件
    private View mHeaderView;

    // 底部控件
    private View mFooterView;


    private boolean isHasHeader = false;

    private boolean isHasFooter = false;


   /**
     * 添加頭部視圖
     * @param header
     */
    public void setHeaderView(View header){
        this.mHeaderView = header;
        isHasHeader = true;
        notifyDataSetChanged();
    }

    /**
     * 添加底部視圖
     * @param footer
     */
    public void setFooterView(View footer){
        this.mFooterView = footer;
        isHasFooter = true;
        notifyDataSetChanged();
    }


}

上面的代碼都很簡單,設置頭部和底部佈局,修改對應的標誌。

定義不同的視圖類型,用以確定viewType的值

   // item 的三種類型
    public static final int ITEM_TYPE_NORMAL = 0X1111; // 正常的item類型
    public static final int ITEM_TYPE_HEADER = 0X1112; // header
    public static final int ITEM_TYPE_FOOTER = 0X1113; // footer

定義了三種類型,因爲對於item條目的類型,無非正常的,頭部,底部三種,當然如果有特殊需求,自行添加。

在getItemViewType()中,根據索引值,返回不同的佈局類型

 @Override
    public int getItemViewType(int position) {

        // 根據索引獲取當前View的類型,以達到複用的目的

        // 根據位置的索引,獲取當前position的類型
        if(isHasHeader&&position==0){
            return ITEM_TYPE_HEADER;
        }
        if(isHasHeader&&isHasFooter&&position==datas.size()+1){
            // 有頭部和底部時,最後底部的應該等於size+!
            return ITEM_TYPE_FOOTER;
        }else if(!isHasHeader&&isHasFooter&&position==datas.size()){
            // 沒有頭部,有底部,底部索引爲size
            return ITEM_TYPE_FOOTER;
        }
        return ITEM_TYPE_NORMAL;
    }

在onCreateViewHolder()中,根據viewType類型構造不同的ViewHolder

  @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        if(viewType==ITEM_TYPE_FOOTER){
            // 如果是底部類型,返回底部視圖
            return new BaseViewHolder(mFooterView);
        }

        if(viewType==ITEM_TYPE_HEADER){
            return new BaseViewHolder(mHeaderView);
        }
        View view = LayoutInflater.from(parent.getContext()).inflate(getLayoutId(),parent,false);
        return new BaseViewHolder(view);
    }

因爲設置了頭部佈局或者是底部佈局,大小要發生不同的變化

 @Override
    public int getItemCount() {
        int size =  datas.size();
        if(isHasFooter)
            size ++;
        if(isHasHeader)
            size++;
        return size;
    }

最後,綁定數據方法的定義

如果看了上一篇博客,會明白通過bindData()方法,將數據的綁定交給了子類,同樣,此時也是如此,不過我們需要添加一些基本的判斷。

 @Override
    public void onBindViewHolder(BaseRecycleAdapter.BaseViewHolder holder, final int position) {

        if(isHasHeader&&isHasFooter){
            // 有頭佈局和底部時,向前便宜一個,且最後一個不能綁定數據
            if(position==0 ||position==datas.size()+1){
                return;
            }
            bindData(holder,position-1);
        }

        if(position!=0&&isHasHeader&&!isHasFooter){
            // 有頂部,但沒有底部
            bindData(holder,position-1);
        }

        if(!isHasHeader&&isHasFooter){
            // 沒有頂部,但有底部
            if(position==datas.size()){
                return;
            }
            bindData(holder,position);
        }

        if(!isHasHeader&&!isHasFooter){
            // 沒有頂部,沒有底部
            bindData(holder,position);
        }

    }

基本的邏輯,當有頭部時,基本的向子類的偏移量要+1,有底部佈局,則最後一個position不會回調。

使用方式

public class PersonActivity extends AppCompatActivity {


    private RecyclerView mRecycler;
    private ArrayList<Person> mPersons;
    private PersonAdapter mAdapter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_person);

        mRecycler = ((RecyclerView) findViewById(R.id.reycler));


        LinearLayoutManager manager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);


        mRecycler.setLayoutManager(manager);


        mPersons = new ArrayList<>();

        for(int i = 0 ;i <50;i++){

            mPersons.add(new Person(i+""));
        }

        mAdapter = new PersonAdapter(mPersons);


        mRecycler.setAdapter(mAdapter);

        // 設置頭部佈局
        mAdapter.setHeaderView(createView(Color.BLUE,"HEADER"));

        // 設置底部佈局
        mAdapter.setFooterView(createView(Color.RED,"FOOTER"));

    }


    /**
     * 創建一個簡單的佈局
     * @param color
     * @param str
     * @return
     */
    public View createView(int color,String str){
        TextView text = new TextView(this);
        RecyclerView.LayoutParams params = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,100);
        text.setLayoutParams(params);
        text.setBackgroundColor(color);
        text.setText(str);
        text.setGravity(Gravity.CENTER);
        return text;
    }
}

BaseRecycleAdapter的源碼

public abstract class BaseRecycleAdapter<T> extends RecyclerView.Adapter<BaseRecycleAdapter.BaseViewHolder> {


    protected List<T> datas;

    public BaseRecycleAdapter(List<T> datas) {
        this.datas = datas;
    }

    // 頭部控件
    private View mHeaderView;

    // 底部控件
    private View mFooterView;


    // item 的三種類型
    public static final int ITEM_TYPE_NORMAL = 0X1111; // 正常的item類型
    public static final int ITEM_TYPE_HEADER = 0X1112; // header
    public static final int ITEM_TYPE_FOOTER = 0X1113; // footer


    private boolean isHasHeader = false;

    private boolean isHasFooter = false;

    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        if(viewType==ITEM_TYPE_FOOTER){
            // 如果是底部類型,返回底部視圖
            return new BaseViewHolder(mFooterView);
        }

        if(viewType==ITEM_TYPE_HEADER){
            return new BaseViewHolder(mHeaderView);
        }
        View view = LayoutInflater.from(parent.getContext()).inflate(getLayoutId(),parent,false);
        return new BaseViewHolder(view);
    }

    @Override
    public void onBindViewHolder(BaseRecycleAdapter.BaseViewHolder holder, final int position) {

        if(isHasHeader&&isHasFooter){
            // 有頭佈局和底部時,向前便宜一個,且最後一個不能綁定數據
            if(position==0 ||position==datas.size()+1){
                return;
            }
            bindData(holder,position-1);
        }

        if(position!=0&&isHasHeader&&!isHasFooter){
            // 有頂部,但沒有底部
            bindData(holder,position-1);
        }

        if(!isHasHeader&&isHasFooter){
            // 沒有頂部,但有底部
            if(position==datas.size()){
                return;
            }
            bindData(holder,position);
        }

        if(!isHasHeader&&!isHasFooter){
            // 沒有頂部,沒有底部
            bindData(holder,position);
        }

    }


    /**
     * 添加頭部視圖
     * @param header
     */
    public void setHeaderView(View header){
        this.mHeaderView = header;
        isHasHeader = true;
        notifyDataSetChanged();
    }

    /**
     * 添加底部視圖
     * @param footer
     */
    public void setFooterView(View footer){
        this.mFooterView = footer;
        isHasFooter = true;
        notifyDataSetChanged();
    }



    @Override
    public int getItemViewType(int position) {

        // 根據索引獲取當前View的類型,以達到複用的目的

        // 根據位置的索引,獲取當前position的類型
        if(isHasHeader&&position==0){
            return ITEM_TYPE_HEADER;
        }
        if(isHasHeader&&isHasFooter&&position==datas.size()+1){
            // 有頭部和底部時,最後底部的應該等於size+!
            return ITEM_TYPE_FOOTER;
        }else if(!isHasHeader&&isHasFooter&&position==datas.size()){
            // 沒有頭部,有底部,底部索引爲size
            return ITEM_TYPE_FOOTER;
        }
        return ITEM_TYPE_NORMAL;
    }

    /**
     * 刷新數據
     * @param datas
     */
    public void refresh(List<T> datas){
        this.datas.clear();
        this.datas.addAll(datas);
        notifyDataSetChanged();
    }


    /**
     * 添加數據
     * @param datas
     */
    public void addData(List<T> datas){
        this.datas.addAll(datas);
        notifyDataSetChanged();
    }

    /**
     *  綁定數據
     * @param holder  具體的viewHolder
     * @param position  對應的索引
     */
    protected abstract void bindData(BaseViewHolder holder, int position);



    @Override
    public int getItemCount() {
        int size =  datas.size();
        if(isHasFooter)
            size ++;
        if(isHasHeader)
            size++;
        return size;
    }




    /**
     * 封裝ViewHolder ,子類可以直接使用
     */
    public class BaseViewHolder extends  RecyclerView.ViewHolder{


        private Map<Integer, View> mViewMap;

        public BaseViewHolder(View itemView) {
            super(itemView);
            mViewMap = new HashMap<>();
        }

        /**
         * 獲取設置的view
         * @param id
         * @return
         */
        public View getView(int id) {
            View view = mViewMap.get(id);
            if (view == null) {
                view = itemView.findViewById(id);
                mViewMap.put(id, view);
            }
            return view;
        }
    }

    /**
     * 獲取子item
     * @return
     */
    public abstract int getLayoutId();



    /**
     * 設置文本屬性
     * @param view
     * @param text
     */
    public void setItemText(View view,String text){
        if(view instanceof TextView){
            ((TextView) view).setText(text);
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章