打造android偷懶神器———RecyclerView的萬能適配器


轉載請注明出處謝謝:http://www.cnblogs.com/liushilin/p/5720926.html

 

很不好意思讓大家久等了,本來昨天就應該寫這個的,無奈公司昨天任務比較緊,所以沒能按時給大家帶來RecyclerView的適配器,樓主對期待的小伙伴表示最深刻地歉意。

如果你沒有看前面的萬能的ListView,GridView等的萬能適配器,樓主推薦你去看一看,當然,大牛就免了。

另外,樓主今天在構思這個RecyclerView的過程中發現前天寫的ListView有點毛病,現在樓主已經更改了,並且重新提交到了github,有需要的小伙伴自己去抓緊看吧。

這里是直通車:http://www.cnblogs.com/liushilin/p/5716306.html

 

RecyclerView也出來這么久了,雖不說大家都耳熟能詳,但是至少還是很有影響力的,畢竟官方是推出來替代過往的ListView,GridView等列表顯示控件的,自然有她神奇的地方。我們肯定得緊跟時代的步伐嘛。

 

對RecyclerView的簡單使用還不了解的小伙伴,我推薦你去看一看我之前寫的RecyclerView的簡單使用,雖說可能不如大牛們寫的面面俱到,全是精髓,但是我相信一定有它存在的意義。傳送門:http://www.cnblogs.com/liushilin/p/5673833.html

 

簡單上個運行圖:

好了,大都不多說。直入今天的正題——偷懶神器,RecyclerView萬能適配器的簡單構造思路。

其實多半還是和之前構造ListView的萬能適配器差不多哈,畢竟RecyclerView就是為了替代它們出現的,只是RecyclerView封裝了ViewHolder而已,而我們要實現把ViewHolder和Adaper封裝成一個萬能的適配器,我們肯定還是得像上篇提到的利用每個view獨一無二的id進行鍵值映射來做處理,當然我們還是用現在官方推薦的SparseArray,這個東西在能替代HashMap的時候真的好用,性能的優化就不用多說了。

 

先看看核心代碼:

我們封裝了ViewHolder,為了把設置值等都封裝進去,我們對外提供了set方法。通過一個getView來實現之前類似於ViewHolder的設置標簽的效果。如果已經綁定,則直接返回,否則放到SparseArray中。

下面是ViewHolder的基本封裝。如果你有之前ListView的ViewHolder的封裝,這個看起來我相信很好理解。

 1 package com.example.nanchen.commonadaperrecyclerdemo;
 2 
 3 import android.content.Context;
 4 import android.graphics.Bitmap;
 5 import android.support.v7.widget.RecyclerView;
 6 import android.util.SparseArray;
 7 import android.view.View;
 8 import android.widget.ImageView;
 9 import android.widget.TextView;
10 
11 import com.squareup.picasso.Picasso;
12 
13 /**
14  * 萬能的RecyclerView的ViewHolder
15  * Created by 南塵 on 16-7-30.
16  */
17 public class BaseRecyclerHolder extends RecyclerView.ViewHolder {
18 
19     private SparseArray<View> views;
20     private Context context;
21 
22     private BaseRecyclerHolder(Context context,View itemView) {
23         super(itemView);
24         this.context = context;
25         //指定一個初始為8
26         views = new SparseArray<>(8);
27     }
28 
29     /**
30      * 取得一個RecyclerHolder對象
31      * @param context 上下文
32      * @param itemView 子項
33      * @return 返回一個RecyclerHolder對象
34      */
35     public static BaseRecyclerHolder getRecyclerHolder(Context context,View itemView){
36         return new BaseRecyclerHolder(context,itemView);
37     }
38 
39     public SparseArray<View> getViews(){
40         return this.views;
41     }
42 
43     /**
44      * 通過view的id獲取對應的控件,如果沒有則加入views中
45      * @param viewId 控件的id
46      * @return 返回一個控件
47      */
48     @SuppressWarnings("unchecked")
49     public <T extends View> T getView(int viewId){
50         View view = views.get(viewId);
51         if (view == null ){
52             view = itemView.findViewById(viewId);
53             views.put(viewId,view);
54         }
55         return (T) view;
56     }
57 
58     /**
59      * 設置字符串
60      */
61     public BaseRecyclerHolder setText(int viewId,String text){
62         TextView tv = getView(viewId);
63         tv.setText(text);
64         return this;
65     }
66 
67     /**
68      * 設置圖片
69      */
70     public BaseRecyclerHolder setImageResource(int viewId,int drawableId){
71         ImageView iv = getView(viewId);
72         iv.setImageResource(drawableId);
73         return this;
74     }
75 
76     /**
77      * 設置圖片
78      */
79     public BaseRecyclerHolder setImageBitmap(int viewId, Bitmap bitmap){
80         ImageView iv = getView(viewId);
81         iv.setImageBitmap(bitmap);
82         return this;
83     }
84 
85     /**
86      * 設置圖片
87      */
88     public BaseRecyclerHolder setImageByUrl(int viewId,String url){
89         Picasso.with(context).load(url).into((ImageView) getView(viewId));
90         //        ImageLoader.getInstance().init(ImageLoaderConfiguration.createDefault(context));
91         //        ImageLoader.getInstance().displayImage(url, (ImageView) getView(viewId));
92         return this;
93     }
94 }

然后是Recycler的Adapter,由於RecyclerView的Adapter必須繼承自RecyclerView.Adapter,並且指定我們寫的ViewHolder為泛型,為了達到萬能的效果,我們把需要傳入的Java Bean屬性直接用一個泛型T指代。

下面這些值得你注意:

1)RecyclerView沒有提供Item的點擊事件,所以我們需要自己自定義,建議實現在Adapter中,因為adapter里面會用到ViewHolder,這樣有助用我們寫每一項的點擊事件。

2)RecyclerView不僅支持全局刷新,而且支持局部刷新,所以我們建議把添加和刪除的方法直接寫在Adapter中。

3)我們為了達到萬能的效果,所以我們把設置holder的方法作為一個抽象方法,方面我們通過viewId傳值到相應的控件中,把整個Adapter變成一個抽象方法,這樣在子類中就可以去通過強制實現的方式把我們的數據填充進去。

還是直接看源碼吧。

  1 package com.example.nanchen.commonadaperrecyclerdemo;
  2 
  3 import android.content.Context;
  4 import android.support.v7.widget.RecyclerView;
  5 import android.view.LayoutInflater;
  6 import android.view.View;
  7 import android.view.ViewGroup;
  8 
  9 import java.util.List;
 10 
 11 /**
 12  * 萬能的RecyclerView適配器
 13  * Created by 南塵 on 16-7-30.
 14  */
 15 public abstract class BaseRecyclerAdapter<T> extends RecyclerView.Adapter<BaseRecyclerHolder> {
 16 
 17     private Context context;//上下文
 18     private List<T> list;//數據源
 19     private LayoutInflater inflater;//布局器
 20     private int itemLayoutId;//布局id
 21     private boolean isScrolling;//是否在滾動
 22     private OnItemClickListener listener;//點擊事件監聽器
 23     private OnItemLongClickListener longClickListener;//長按監聽器
 24     private RecyclerView recyclerView;
 25 
 26     //在RecyclerView提供數據的時候調用
 27     @Override
 28     public void onAttachedToRecyclerView(RecyclerView recyclerView) {
 29         super.onAttachedToRecyclerView(recyclerView);
 30         this.recyclerView = recyclerView;
 31     }
 32 
 33     @Override
 34     public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
 35         super.onDetachedFromRecyclerView(recyclerView);
 36         this.recyclerView = null;
 37     }
 38 
 39     /**
 40      * 定義一個點擊事件接口回調
 41      */
 42     public interface OnItemClickListener {
 43         void onItemClick(RecyclerView parent, View view, int position);
 44     }
 45 
 46     public interface OnItemLongClickListener {
 47         boolean onItemLongClick(RecyclerView parent, View view, int position);
 48     }
 49 
 50     /**
 51      * 插入一項
 52      *
 53      * @param item
 54      * @param position
 55      */
 56     public void insert(T item, int position) {
 57         list.add(position, item);
 58         notifyItemInserted(position);
 59     }
 60 
 61     /**
 62      * 刪除一項
 63      *
 64      * @param position 刪除位置
 65      */
 66     public void delete(int position) {
 67         list.remove(position);
 68         notifyItemRemoved(position);
 69     }
 70 
 71     public BaseRecyclerAdapter(Context context, List<T> list, int itemLayoutId) {
 72         this.context = context;
 73         this.list = list;
 74         this.itemLayoutId = itemLayoutId;
 75         inflater = LayoutInflater.from(context);
 76 
 77         //        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
 78         //            @Override
 79         //            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
 80         //                super.onScrollStateChanged(recyclerView, newState);
 81         //                isScrolling = !(newState == RecyclerView.SCROLL_STATE_IDLE);
 82         //                if (!isScrolling) {
 83         //                    notifyDataSetChanged();
 84         //                }
 85         //            }
 86         //        });
 87     }
 88 
 89     @Override
 90     public BaseRecyclerHolder onCreateViewHolder(ViewGroup parent, int viewType) {
 91         View view = inflater.inflate(itemLayoutId, parent, false);
 92         return BaseRecyclerHolder.getRecyclerHolder(context, view);
 93     }
 94 
 95     @Override
 96     public void onBindViewHolder(final BaseRecyclerHolder holder, int position) {
 97 
 98         if (listener != null){
 99             holder.itemView.setBackgroundResource(R.drawable.recycler_bg);//設置背景
100         }
101         holder.itemView.setOnClickListener(new View.OnClickListener() {
102             @Override
103             public void onClick(View view) {
104                 if (listener != null && view != null && recyclerView != null) {
105                     int position = recyclerView.getChildAdapterPosition(view);
106                     listener.onItemClick(recyclerView, view, position);
107                 }
108             }
109         });
110 
111 
112         holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
113             @Override
114             public boolean onLongClick(View view) {
115                 if (longClickListener != null && view != null && recyclerView != null) {
116                     int position = recyclerView.getChildAdapterPosition(view);
117                     longClickListener.onItemLongClick(recyclerView, view, position);
118                     return true;
119                 }
120                 return false;
121             }
122         });
123 
124         convert(holder, list.get(position), position, isScrolling);
125 
126     }
127 
128     @Override
129     public int getItemCount() {
130         return list == null ? 0 : list.size();
131     }
132 
133     public void setOnItemClickListener(OnItemClickListener listener) {
134         this.listener = listener;
135     }
136 
137     public void setOnItemLongClickListener(OnItemLongClickListener longClickListener) {
138         this.longClickListener = longClickListener;
139     }
140 
141     /**
142      * 填充RecyclerView適配器的方法,子類需要重寫
143      *
144      * @param holder      ViewHolder
145      * @param item        子項
146      * @param position    位置
147      * @param isScrolling 是否在滑動
148      */
149     public abstract void convert(BaseRecyclerHolder holder, T item, int position, boolean isScrolling);
150 }

注意其中我的抽象方法給了一個isScrolling的參數,我的目的是想控制滑動的時候不加載圖片。目前這個還沒實現,所以大家可以在自己封裝的時候不去寫它,當然,你有思考的話我建議大家最好實現吧,另外別忘了告訴樓主哦~~嘿嘿。樓主就是這樣的謙(zhuang)虛(bi)。

 

其他的代碼就很簡單了,java bean類Data和布局和昨天一樣的,大家可以自己去隨便怎么布局。

這里只上一個MainActivity的代碼,有需要的大家可以去github提取:https://github.com/nanchen2251/CommonAdapterRecyclerDemo

 

package com.example.nanchen.commonadaperrecyclerdemo;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

public class MainActivity extends AppCompatActivity {

    private List<Data> list;
    private RecyclerView recyclerView;
    private BaseRecyclerAdapter<Data> adapter;
    private EditText text;

    @SuppressWarnings("unchecked")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        list = new ArrayList<>();

        initList();

        adapter = new BaseRecyclerAdapter<Data>(this,list,R.layout.list_item) {
            @Override
            public void convert(BaseRecyclerHolder holder, Data item, int position, boolean isScrolling) {
                holder.setText(R.id.item_text,item.getText());
                if (item.getImageUrl() != null){
                    holder.setImageByUrl(R.id.item_image,item.getImageUrl());
                }else {
                    holder.setImageResource(R.id.item_image,item.getImageId());
                }
            }

        };

        adapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(RecyclerView parent, final View view, int position) {
                Toast.makeText(MainActivity.this, String.format(Locale.CHINA,"你點擊了第%d項,長按會刪除!",position),Toast.LENGTH_SHORT).show();
            }
        });

        adapter.setOnItemLongClickListener(new BaseRecyclerAdapter.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(RecyclerView parent, View view, int position) {
                adapter.delete(position);
                return true;
            }
        });

        text = (EditText) findViewById(R.id.main_text);
        recyclerView = (RecyclerView) findViewById(R.id.main_recycler);
        recyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));
        recyclerView.setAdapter(adapter);



    }

    public void initList(){
        for (int i = 0; i < 5; i++) {
            list.add(new Data("本地 "+i,R.mipmap.ic_launcher));
        }
        for (int i = 0; i < 5; i++) {
            list.add(new Data("網絡 "+i,"http://pic.cnblogs.com/face/845964/20160301162812.png"));
        }
    }

    public void btnClick(View view) {
        String string = text.getText().toString().trim();
        Data data = new Data(string,R.mipmap.ic_launcher);
//        list.add(list.size()/2,data);
        adapter.insert(data,list.size()/2);

        Toast.makeText(MainActivity.this,list.size()+"",Toast.LENGTH_SHORT).show();
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM