轉載請注明出處謝謝: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(); } }