RecyclerView使用以及避坑指南
基本使用
-
继承
RecyclerView.Adapter
,并在内部自定义对应的ViewHolder
public class HelloRecyclerAdapter extends RecyclerView.Adapter<HelloRecyclerAdapter.ViewHolder> { private List dataList = new ArrayList(); public HelloRecyclerAdapter(List dataList) { this.dataList = dataList; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_hello_recycler, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { Object data = dataList.get(position); //把data的数据赋值到holder对应的View中 } @Override public int getItemCount() { return dataList.size(); } class ViewHolder extends RecyclerView.ViewHolder { public ViewHolder(View itemView) { super(itemView); } } }
-
Activity
中新建adapter
并赋值给recycler
recyclerView.setLayoutManager(new LinearLayoutManager(this)); //list是已赋值数据 recyclerView.setAdapter(new HelloRecyclerAdapter(list));
以上,一个
recyclerView
的列表就出来了。
RecyclerView
中添加header
-
先定义两个常量,用来区分
header
和普通情况private static final int HEADER = 1; private static final int NORMAL = 2;
-
建立另一个
HeaderViewHolder
class HeaderViewHolder extends RecyclerView.ViewHolder { public HeaderViewHolder(View itemView) { super(itemView); } }
-
然后
onCreateViewHolder
中分情况创建public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view; if (viewType == HEADER) { view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_hello_recycler, parent, false); } else { view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_hello_recycler_head, parent, false) } return new ViewHolder(view); }
-
getItemCount
中增加对应数量@Override public int getItemCount() { //增加header节点 return dataList.size() + 1; }
-
onBindViewHolder
中分情况绑定数据@Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { Object data = dataList.get(position); if (holder instanceof HeaderViewHolder) { //绑定header数据 } else { //绑定普通数据 } }
当然这只是最基本的能用了,在使用过程中会遇到一些奇奇怪怪的问题,接下来我们一一解决
recyclerView
中数据错乱问题
-
数据来源固定
这种情况很好解决,主要就是每个
item
在赋值时候,要把各个情况都覆盖到,不能只覆盖一种情况。假设
item
内有收藏功能,收藏后会用UI的变化,我们可以用如下方法实现:@Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { Object data = dataList.get(position); //这里一定不能只写if而不写else,否则会出现颜色错乱 if(data.isCollection()){ holder.collectionView.setTextColor(RED) }else{ holder.collectionView.setTextColor(BLACK) } }
-
数据来自网络
这类情况的常见情景是加载图片,当网络环境较差时,图片加载完后,你可能已经翻页了,这时候再去显示图片会出现错乱的情况。
针对这一情况,我们可以给图片添加一个TAG,显示图片时候判断对应的TAG是否一致
holder.ivCameraImages.setBackground(R.drawable.place_holder); holder.ivCameraImages.setTag(imageURL); @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == MSG_IMAGE) { Bitmap bm = (Bitmap) msg.obj; if (bm != null) { if (TextUtils.equals((String) imageView.getTag(), imageURL)){ imageView.setBackground(new BitmapDrawable(bm)); } } } }
当然,目前的主流图片框架会把url作为对应的TAG添加到ImageView中,省了我们重复造轮子的时间。
RecyclerView
中的局部刷新
还是回到那个收藏的问题,如果点击收藏按钮,只更新当前的item
这个需求如何实现?
-
当列表中没有图片时候
public void collectionSuccess(String id) { //当header的收藏数量变化时候 //通过ID找到下标 int position = findPositionForId(id); //更新数量 dataList.get(position) .setCollectionNum(data.getCollectionNum()+1); //更新对应UI notifyItemChanged(position); }
-
列表中存在图片时
当有图片时候,使用
notifyItemChanged()
方法会导致图片闪烁。解决这个问题呢就需要进行一些大的改动了。
首先,放弃使用两个参数的
onBindViewHolder
方法,使用三个参数的方法:@Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { //不去覆写这个方法 } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List<Object> payloads) { if (payLoads != null && payLoads.size() > 0) { //有部分数据更新的情况 String s = (String) payLoads.get(0); switch (s) { case STAR: //点赞数量UI更新 updateStar(holder, dataList.get(position - 1)); break; case COLLECTION: //收藏数量UI更新 updateCollection(holder, dataList.get(position - 1)); break; } } else { //正常情况 } }
然后,数据发生改变时,调用的方法也有变化
public void collectionSuccess(String id) { //当header的收藏数量变化时候 //通过ID找到下标 int position = findPositionForId(id); //更新数量 dataList.get(position).setCollectionNum(data.getCollectionNum()+1); //更新对应UI notifyItemChanged(position,COLLECTION); }
这样设置完后,局部更新数据就不会导致item有变化了。
最后,我把整个带Heade
r的,可以局部更新UI的adapter
基本框架都放出来,大家有需要的自行取用
完整结构
public class HelloRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int HEADER = 1;
private static final int NORMAL = 2;
private static final String COLLECTION = "collection";
private static final String STAR = "star";
private Context context;
private Object headData;
private List dataList;
public HelloRecyclerAdapter(Context mContext,
Object headerData,
List answerList) {
this.context = mContext;
this.headData = headerData;
this.dataList = answerList;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == HEADER) {
View view = LayoutInflater.from(context)
.inflate(R.layout.item_header_view, parent, false);
return new HeaderViewHolder(view);
} else {
View view = LayoutInflater.from(context)
.inflate(R.layout.item_normal_view, parent, false);
return new NormalViewHolder(view);
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
//不去覆写这个方法
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List<Object> payloads) {
if (holder instanceof HeaderViewHolder) {
bindHeaderView((HeaderViewHolder) holder, payloads);
} else {
bindNormalView((NormalViewHolder) holder, position, payloads);
}
}
private void bindHeaderView(HeaderViewHolder holder, List<Object> payLoads) {
if (payLoads != null && payLoads.size() > 0) {
String payLoad = (String) payLoads.get(0);
switch (payLoad) {
case COLLECTION:
//header收藏数量更新
updateCollection(holder);
break;
case STAR:
//header点赞数量更新
updateStar(holder);
break;
}
} else {
//正常绑定数据
}
}
private void bindNormalView(NormalViewHolder holder, int position, List<Object> payLoads) {
if (payLoads != null && payLoads.size() > 0) {
String s = (String) payLoads.get(0);
switch (s) {
case STAR:
//点赞数量更新
updateStar(holder, dataList.get(position - 1));
break;
case COLLECTION:
//收藏数量更新
updateCollection(holder, dataList.get(position - 1));
break;
}
} else {
//绑定正常数据
}
}
@Override
public int getItemCount() {
return dataList.size() + 1;
}
@Override
public int getItemViewType(int position) {
if (position == 0) {
return HEADER;
} else {
return NORMAL;
}
}
public void collectionSuccess(String id) {
//当header的收藏数量变化时候
//通过ID找到下标
int position = findPositionForId(id);
//更新数量
dataList.get(position).setCollectionNum(data.getCollectionNum()+1);
//更新对应UI
notifyItemChanged(position,COLLECTION);
}
public void disCollectionSuccess(String id) {
//当header的收藏数量变化时候
notifyItemChanged(0, COLLECTION);
}
public void starSuccess(String id) {
int position = 0;
//略去通过ID寻找position过程
//略去通过ID更改对应数据的点赞数量
notifyItemChanged(position, STAR);
}
public void disStarSuccess(String id) {
int position = 0;
//略去通过ID寻找position过程
//略去通过ID更改对应数据的点赞数量
notifyItemChanged(position, STAR);
}
class HeaderViewHolder extends RecyclerView.ViewHolder {
public HeaderViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
class NormalViewHolder extends RecyclerView.ViewHolder {
public NormalViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}