YCBaseAdapter封裝控件

目錄介紹

  • 1.關於需求介紹git

    • 1.1 需求有這些
    • 1.2 封裝理念
  • 2.簡單封裝【V1.0版本】github

    • 2.1 封裝簡單的ViewHolder
    • 2.2 封裝簡單RecyclerView.Adapter
    • 2.3 如何使用通用adapter
    • 2.4 如何使佈局多樣化編程

      • 作法
      • 原理
      • a.定義一個接口,判斷返回數據類型
      • b.修改封裝adapter中getItemViewType中代碼
      • c.修改adapter,實現自定義接口
      • d.在Activity中設置參數location【定義類型參數】
  • 3.簡單封裝困境緩存

    • 3.1 遇到問題與困境
    • 3.2 用以前封裝類實現多種類型佈局,出現的弊端
  • 4.關於複雜界面封裝網絡

    • 4.1 具體能夠看YCRefreshView

0.備註

1.關於需求介紹

  • RecycleView能夠知足諸多功能,封裝公用的adapter,提升編程效率

1.1 關於需求,大概有這些:

  • 數據的綁定,刷新
  • 多種不一樣類型的數據綁定
  • 優雅添加頭佈局或者底佈局
  • 增長onItemClickListener , onItenLongClickListener
  • 支持加載相應type錯誤頁面,無數據頁面
  • 支持集合set,add,remove,clear等操做刷新

1.2 封裝理念

  • 構造一個通用的Adapter模版,避免每添加一個列表就要寫一個Adapter,避免寫Adapter中的大量重複代碼
  • 高內聚,低耦合,擴展方便
  • 經過組裝的方式來構建Adapter,將每一種(ViewType不一樣的)Item抽象成一個單獨組件,Adapter 就是一個殼,咱們只須要向Adapter中添加Item就行,這樣作的好處就是減小耦合,去掉一種item 或者添加一種item對於列表是沒有任何影響的

2.簡單封裝

2.1 封裝簡單的ViewHolder

  • 首先,繼承 RecyclerView.ViewHolder 實現一個通用的 ViewHolder當中,使用 SparseArray 來存放 View 以減小 findViewById 的次數,SparseArray 比 HashMap 更省內存,在某些條件下性能會更好,不過只能存儲 key 爲 int 類型的數據,正好用來存放資源ID
  • 由於列表項中通常都是使用 TextView,ImageView 等控件,因此這裏提供控件的操做方法。此外,爲了監聽列表項單擊和雙擊事件,這裏再來自定義一個接口 onItemCommonClickListener ,用於點擊事件回調
/**
 * ================================================
 * 做    者:楊充
 * 版    本:1.0
 * 建立日期:2016/3/9
 * 描    述:ViewHolder的抽取類
 * 修訂歷史:
 * ================================================
 */
public class BaseViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {

    // SparseArray 比 HashMap 更省內存,在某些條件下性能更好,只能存儲 key 爲 int 類型的數據,
    // 用來存放 View 以減小 findViewById 的次數
    private SparseArray<View> viewSparseArray;
    //這個是item的對象
    private View mItemView;

    public BaseViewHolder(View itemView) {
        super(itemView);
        this.mItemView = itemView;
        itemView.setOnClickListener(this);
        itemView.setOnLongClickListener(this);
        viewSparseArray = new SparseArray<>();
    }

    /**
     * 根據 ID 來獲取 View
     * @param viewId viewID
     * @param <T>    泛型
     * @return 將結果強轉爲 View 或 View 的子類型
    */
    public <T extends View> T getView(int viewId) {
        // 先從緩存中找,找打的話則直接返回
        // 若是找不到則 findViewById ,再把結果存入緩存中
        View view = viewSparseArray.get(viewId);
        if (view == null) {
            view = itemView.findViewById(viewId);
            viewSparseArray.put(viewId, view);
        }
        return (T) view;
    }

    /**
     * 獲取item的對象
     */
    public View getItemView(){
        return mItemView;
    }


    /**
     * 設置TextView的值
     */
    public BaseViewHolder setText(int viewId, String text) {
        TextView tv = getView(viewId);
        tv.setText(text);
        return this;
    }

    /**
     * 設置imageView圖片
     */
    public BaseViewHolder setImageResource(int viewId, int resId) {
        ImageView view = getView(viewId);
        view.setImageResource(resId);
        return this;
    }

    /**
     * 設置imageView圖片
     */
    public BaseViewHolder setImageBitmap(int viewId, Bitmap bitmap) {
        ImageView view = getView(viewId);
        view.setImageBitmap(bitmap);
        return this;
    }



    @Override
    public void onClick(View v) {
        if (commonClickListener != null) {
            commonClickListener.onItemClickListener(getAdapterPosition());
        }
    }

    @Override
    public boolean onLongClick(View v) {
        if (commonClickListener != null) {
            commonClickListener.onItemLongClickListener(getAdapterPosition());
        }
        return false;
    }

    public interface onItemCommonClickListener {
        void onItemClickListener(int position);
        void onItemLongClickListener(int position);
    }


    private onItemCommonClickListener commonClickListener;
    public void setCommonClickListener(onItemCommonClickListener commonClickListener) {
        this.commonClickListener = commonClickListener;
    }

}

2.2 封裝簡單RecyclerView.Adapter

  • 由於不知道要使用到的數據類型是哪種,也爲了更好的適配各類數據類型,因此這裏須要用到泛型當中,onBindViewHolder(CommonViewHolder holder, int position) 須要咱們本身來操做,因此這裏再來聲明一個抽象方法 bindData(CommonViewHolder holder, T data) ,由子類來負責實現綁定操做
  • 添加簡單的設置數據,清理數據,移除數據的方法
/**
 * ================================================
 * 做    者:楊充
 * 版    本:1.0
 * 建立日期:2016/3/9
 * 描    述:RecycleView的adapter抽取類
 * 修訂歷史:
 * ================================================
 */
public abstract class BaseAdapter<T> extends RecyclerView.Adapter<BaseViewHolder> {

    private Context context;
    private int layoutId;
    private List<T> data;

    /**
     * 構造方法
     * @param layoutId      佈局
     * @param context       上下文
     */
    public BaseAdapter(Context context , int layoutId) {
        this.layoutId = layoutId;
        this.context = context;
        data = new ArrayList<>();
    }

    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(layoutId, parent, false);
        return new BaseViewHolder(view);
    }

    @Override
    public void onBindViewHolder(BaseViewHolder holder, int position) {
        if(data!=null && data.size()>0){
            bindData(holder, data.get(position));
        }
    }

    @Override
    public int getItemViewType(int position) {
        return super.getItemViewType(position);
    }

    @Override
    public int getItemCount() {
        return data==null ? 0 : data.size();
    }


    /**
     * 當子類adapter繼承此BaseAdapter時,須要子類實現的綁定數據方法
     */
    protected abstract void bindData(BaseViewHolder holder, T t);

    /**
     * 設置數據,而且刷新頁面
     */
    public void setData(List<T> list){
        data.clear();
        if(list==null || list.size()==0){
            return;
        }
        data.addAll(list);
        notifyItemRangeChanged(data.size()-list.size(),data.size());
        notifyDataSetChanged();
    }

    /**
     * 獲取數據
     */
    public List<T> getData(){
        return data;
    }

    /**
     * 清理全部數據,而且刷新頁面
     */
    public void clear(){
        data.clear();
        notifyDataSetChanged();
    }

    /**
     * 移除數據
     */
    public void remove(T t){
        if(data.size()==0){
            return;
        }
        int index = data.indexOf(t);
        remove(index);
    }


    /**
     * 移除數據
     */
    public void remove(int index){
        if(data.size()==0){
            return;
        }
        data.remove(index);
        notifyItemRemoved(index);
    }

    /**
     * 移除數據
     */
    public void remove(int start,int count){
        if(data.size()==0){
            return;
        }
        if((start +count) > data.size()){
            return;
        }
        data.subList(start,start+count).clear();
        notifyItemRangeRemoved(start,count);
    }
}

2.3 如何使用通用adapter

  • 須要先來繼承 XXXAdapter ,只須要實現一個方法便可,看起來簡潔多了吧。代碼中聲明瞭兩個構造函數,根據是否須要用到點擊事件監聽來選擇
public class SecondAdapter extends BaseAdapter<String> {

    private BaseViewHolder.onItemCommonClickListener commonClickListener;

    public SecondAdapter(Context context) {
        super(context,R.layout.item_first);

    }

    public SecondAdapter(Context context, BaseViewHolder.onItemCommonClickListener commonClickListener) {
        super(context, R.layout.item_first);
        this.commonClickListener = commonClickListener;
    }

    @Override
    protected void bindData(BaseViewHolder holder, String s) {
        holder.setText(R.id.tv_title,s);
        //TextView view = holder.getView(R.id.tv_title);
        //view.setText(s);
        holder.setCommonClickListener(commonClickListener);
    }
}

2.4 如何使佈局多樣化

  • 抽取的adapter已經能夠爲咱們節省不少代碼了,免去了一些重複性操做。可是若是list列表有多種類型,好比像聊天界面,有聊天文字,圖片,文件,紅點等多種不一樣的佈局。那麼添加使用不一樣佈局的功能十分重要。
  • 作法:
  • 複寫getItemViewType,根據咱們的bean去返回不一樣的類型
  • onCreateViewHolder中根據itemView去生成不一樣的ViewHolder
  • 定義一個接口,判斷返回數據類型 須要有一個方法來判斷哪一種數據類型須要使用哪一種佈局,因此再來定義一個接口,getLayoutId() 用於返會佈局文件ID
public interface MultiTypeSupport<T> {
    int getLayoutId(T item, int position);
}
  • b.修改封裝adapter中getItemViewType中代碼
  • 修改 XXXAdapter。若是 multiTypeSupport 不爲 null,意思就是要使用到不一樣的佈局文件了,則調用 getLayoutId() 方法,將其返回值做爲 ItemViewType
@Override
public int getItemViewType(int position) {
    if (multiTypeSupport != null) {
        return multiTypeSupport.getLayoutId(data.get(position), position);
    }
    return super.getItemViewType(position);
}

@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (multiTypeSupport != null) {
        layoutId = viewType;
    }
    View view = LayoutInflater.from(context).inflate(layoutId, parent, false);
    return new BaseViewHolder(view);
}
  • c.修改adapter,實現自定義接口
  • 修改Adapter 類,實現 MultiTypeSupport 接口,根據 T 對象的 location 字段的值,來決定返回哪一個佈局文件的ID
@Override
public int getLayoutId(ThirdBean item, int position) {
    if (item.getLocation()==1) {
        return R.layout.main_chat_from_msg;
    }
    return R.layout.main_chat_send_msg;
}

public ThirdAdapter(Context context, BaseViewHolder.onItemCommonClickListener commonClickListener) {
    super(context, R.layout.main_chat_from_msg);
    this.commonClickListener = commonClickListener;
    this.multiTypeSupport = this;
}
  • d.在Activity中設置參數location【定義類型參數】
list = new ArrayList<>();
for(int a=0 ; a<13 ; a++){
    if(a==3 || a==8 || a==10 || a==12){
        list.get(a).setTitle("這個是假數據"+a);
        list.get(a).setLocation(1);
    }else {
        list.get(a).setTitle("這個是假數據"+a);
        list.get(a).setLocation(2);
    }
}

3.迭代封裝

3.1 遇到問題與困境

  • 前面,咱們能夠簡單實現不一樣佈局類型的。可是大多數的App首頁都是比較複雜的,好比一個社交APP的首頁,包含Banner區、廣告區、文本內容、圖片內容、視頻內容等等。RecyclerView 能夠用ViewType 來區分不一樣的item,也能夠知足需求 ,但仍是存在一些問題。ide

    • 0,若是type的部分實體類參數不一樣,如何傳遞setData。即便合併了實體類,可是維護起來十分困難。
    • 1,在item過多邏輯複雜列表界面,Adapter裏面的代碼量龐大,邏輯複雜,後期難以維護
    • 2,每次增長一個列表都須要增長一個Adapter,重複搬磚,效率低下。

3.2 用以前封裝類實現多種類型佈局,出現的弊端

  • 0.傳遞進來的實體類只能是一種,若是處理多種類型的參數不相同,那麼合併實體類容易出問題
  • 1.下面這樣就是咱們一般寫一個多Item列表的方法,根據不一樣的ViewType 處理不一樣的item,若是邏輯複雜,這個類的代碼量是很龐大的。若是版本迭代添加新的需求,修改代碼很麻煩,後期維護困難。
public class FourAdapter extends BaseAdapter<FourBean> implements MultiTypeSupport<FourBean>{

    public FourAdapter(Context context) {
        super(context, R.layout.main_chat_from_msg);
        //這句話一點要添加
        this.multiTypeSupport = this;
    }

    @Override
    protected void bindData(BaseViewHolder holder, FourBean s) {
        int location = s.getLocation();
        switch (location){
            case 1:     //處理頭部佈局邏輯
                holder.setText(R.id.tv_title,s.getTitle());
                break;
            case 2:     //文本邏輯處理
                holder.setText(R.id.tv_title,s.getTitle());
                break;
            case 3:     //圖片邏輯處理
                holder.setText(R.id.tv_title,s.getTitle());
                break;
            case 4:     //處理底部佈局邏輯
                holder.setText(R.id.tv_title,s.getTitle());
                break;
        }

    }

    @Override
    public int getLayoutId(FourBean item, int position) {
        if (item.getLocation()==1) {
            return R.layout.main_chat_from_msg;
        }else if(item.getLocation()==2){
            return R.layout.item_first;
        } else if(item.getLocation()==4){
            return R.layout.view_footer;
        }
        return R.layout.main_chat_send_msg;
    }
}

4.關於複雜界面封裝

4.1 具體能夠看YCRefreshView

  • **自定義支持上拉加載更多,下拉刷新,支持自由切換狀態【加載中,加載成功,加載失敗,沒網絡等狀態】的控件,拓展功能[支持長按拖拽,側滑刪除]能夠選擇性添加

。具體使用方法,能夠直接參考demo。**函數

  • 輕量級側滑刪除菜單,支持recyclerView,listView,直接嵌套item佈局便可使用,整個側滑菜單思路是:跟隨手勢將item向左滑動
  • 該庫已經用到了實際開發項目中,會持續更新而且修改bug。若是以爲能夠,能夠star一下,多謝支持!
  • 感謝前輩大神們案例及開源分享精神。
  • 一行代碼集成:compile 'org.yczbj:YCRefreshViewLib:2.4'
  • 項目地址:https://github.com/yangchong2...
  • GitHub地址:https://github.com/yangchong211
相關文章
相關標籤/搜索