自从Google推出MD之后,受到了广大程序员的喜爱。其中,最常用的莫过于RecyclerView,相对于传统的ListView而言,RecyclerView的功能更加的强大,其自身提供的LayoutManager可以对Item所显示的样式进行快速切换。好了,废话不多讲,我们就一起来看看吧。注意:我今天并不会讲RecyclerView的基本用法,我相信RecyclerView的基本用法是每个人都会的,所有我在这里就不再啰嗦了。
在使用RecyclerView的时候,我们写适配器的时候一般写法如下:
class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>
{
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
MyViewHolder holder = new MyViewHolder(LayoutInflater.from(
HomeActivity.this).inflate(R.layout.item_home, parent,
false));
return holder;
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position)
{
holder.tv.setText(mDatas.get(position));
}
@Override
public int getItemCount()
{
return mDatas.size();
}
class MyViewHolder extends ViewHolder
{
TextView tv;
public MyViewHolder(View view)
{
super(view);
tv = (TextView) view.findViewById(R.id.id_num);
}
}
}
那么我们现在先来分析一下这个Adapter,注意这个Adapter是由以下的几个部分组成。
1》ViewHolder
2》onBindViewHolder()
3》onCreateViewHolder()
4》getItemCount()
所谓打造一个万能的Adapter适配器,其实就是对这几个方法的再度封装。那么下面我们就一个一个的来分析下如何对这些方法进行封装。
1. ViewHolder
public class ViewHolder extends RecyclerView.ViewHolder {
// 存放所有的控件
private SparseArray<View> mViews;
public ViewHolder(View itemView) {
super(itemView);
mViews = new SparseArray<>();
}
// 获取每个控件
public <T extends View> T getView(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = itemView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
// 设置TextView(采用链式调用的方式)
public ViewHolder setText(int viewId, CharSequence text) {
TextView tv = getView(viewId);
tv.setText(text);
return this;
}
// 设置ImageView(本地资源)
public ViewHolder setImageView(int viewId, int resourceId) {
ImageView imageView = getView(viewId);
imageView.setImageResource(resourceId);
return this;
}
// 设置ImageView(url)
// 遵循开闭原则,我们在这里最好不要写死
// Glide.with(context).load(imageUrl).into(imageView);
public ViewHolder setImageView(int viewId, HolderImageView holdImageView) {
ImageView imageView = getView(viewId);
holdImageView.loadImage(holdImageView.getPath(), imageView);
return this;
}
// 使用静态类供外部使用
public abstract static class HolderImageView {
String imageUrl;
public HolderImageView(String imageUrl) {
this.imageUrl = imageUrl;
}
public abstract void loadImage(String imageUrl, ImageView imageView);
public String getPath() {
return imageUrl;
}
}
}
首先我们建一个类ViewHolder继承自RecyclerView.ViewHolder,当然我们必须要创建一个方法供外部调用获取到控件。那么这个方法就是getView方法,只需要传递一个控件所对应的id就能通过findViewById来拿到控件,在这里我们就要想了?如果我们的id相同,那么我们有没有什么办法不要每次都findViewById。哈哈哈,方法肯定是有,如上面所示,我们创建一个SparseArray来保存每次的view就可以了,实现过程请看上面的代码。在这个方法里面我还对常用的TextView和ImageView进行了处理,一看就懂,当然,我用到了当前比较火的一种方式,即链式调用的方式,不懂的童靴们赶快去学习一下哟,这个真的很好用。
2.getItemCount
这个方法没有什么好讲的,也无须进行封装
3.onBindViewHolder
大家都知道,这个方法其实是展示数据的方法,那么在这里我们就不能直接展示数据,否则的话封装就无从谈起。我们需要把这个方法通过抽象的方法传递出去,供具体的实现者来调用,实现方法也很简单
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
convert(holder, list.get(position), position);
}
// 将展示的数据向外传递
public abstract void convert(ViewHolder holder, T t, int position);
这里我要讲一下这个 T,大家都知道,T其实就是泛型,因为在这个里面我们并不知道外面的数据的具体采用的格式,可能是Bean,也可能是map等等,所以在这里我们只能采用泛型来实现数据的分离。
4.onCreateViewHolder
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(viewId, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
注意,这里的viewId就是我们要加载的布局文件,我们把它声明为全局变量,然后在构造函数里面进行获取,这样,我们要显示的布局就可以通过外部来进行传递了,这样也就实现了布局分离的效果。
看到这里有些人可能已经懂了,有些人可能还是有些疑惑,为了让大家更方便快速的搞懂,我先把封装的那个adapter的全部代码先贴出来
**
* Created by zhoufan on 2018/1/13.
* 打造万能的适配器
* 1.分离ViewHolder
* 2.实现数据分离
* 3.实现布局分离
* 4.实现多布局效果
*/
public abstract class CommonAdapter<T> extends RecyclerView.Adapter<ViewHolder> {
// 实现数据分离,采用泛型
private List<T> list;
// 布局通过外面传递进来
private int viewId;
private LayoutInflater inflater;
// 支持多布局
private MultiItemSupport multiItemSupport;
public CommonAdapter(Context context, List<T> list, int viewId) {
this.list = list;
this.viewId = viewId;
inflater = LayoutInflater.from(context);
}
public CommonAdapter(Context context, List<T> list, MultiItemSupport multiItemSupport) {
this(context, list, -1);
this.multiItemSupport = multiItemSupport;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (multiItemSupport != null) {
viewId = viewType;
}
View view = inflater.inflate(viewId, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
convert(holder, list.get(position), position);
// 设置点击事件
if (onItemClick != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onItemClick.onItemClick(position);
}
});
}
// 设置长按点击事件
if (onItemLongClick != null) {
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return onItemLongClick.onItemLongClick(position);
}
});
}
}
@Override
public int getItemViewType(int position) {
if (multiItemSupport != null) {
return multiItemSupport.getItemView(position);
}
return super.getItemViewType(position);
}
// 将展示的数据向外传递
public abstract void convert(ViewHolder holder, T t, int position);
@Override
public int getItemCount() {
return list != null ? list.size() : 0;
}
// 设置Item的点击事件
private OnItemClickListener onItemClick;
public void setOnItemClickListener(OnItemClickListener onItemClick) {
this.onItemClick = onItemClick;
}
// 设置Item的长按点击事件
private OnItemLongClickListener onItemLongClick;
public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClick) {
this.onItemLongClick = onItemLongClick;
}
}
最后我要讲解的一点就是关于多布局的问题,我们都知道,在实际的开发过程中,多布局是很普遍的,那么当我们涉及到多布局的时候我们应该怎么处理呢?首先,我们需要复写getItemViewType方法,然后判断当前是否有多布局,如果有,那么我们获取到当前位置的布局,然后进行向下传递。因为onCreateViewHolder是在getItemViewType后执行的,而getItemViewType的返回值等于onCreateViewHolder的viewType,所以我们只需要对viewType进行处理即可。
接下来就是重点了,当我们封装完了之后如何使用呢?哈哈,其实使用也是so easy的,不信,你瞧
/**
* Created by zhoufan on 2018/1/13.
* 具体的实现类
*/
public class Adapter extends CommonAdapter {
public Adapter(Context context, final List<Map<String, Object>> list) {
super(context, list, new MultiItemSupport() {
@Override
public int getItemView(int position) {
if (list.get(position).get("flag").toString().equals("0")) {
return R.layout.item_me;
} else {
return R.layout.item_friend;
}
}
});
}
@Override
public void convert(ViewHolder holder, Object data, int position) {
Map<String, Object> map = (Map<String, Object>) data;
holder.setText(R.id.name, String.valueOf(map.get("name")))
.setText(R.id.age, String.valueOf(map.get("age")));
}
}
哈哈,是不是很简单,至此,一个万能的adapter就完成了,在以后的开发过程中,我们就只需调用convert方法来展示数据就可以了