这篇文章是下面文章的补充
线性recyclerview常规用法
1.如果大量的数据被修改或者被修改数据的位置不确定,则使用
adapter.notifyDataSetChanged();
这个方法很消耗性能,不到万不得已不要使用,请尽量使用下面的刷新方法。
2.刷新某一项(常用)
//刷新某Item中的所有组件
adapter.notifyItemChanged(position);
//刷新某Item中的部分组件
adapter.notifyItemChanged(position, payloads);
//插入Item
adapter.notifyItemInserted(position);
//删除Item
adapter.notifyItemRemoved(position);
//移动Item
adapter.notifyItemMoved(position, position + 1);
![](https://img.haomeiwen.com/i12648131/66b23cc8d228120d.png)
需求1:点击按钮1,使图片和文字改变。
switch (view.getId()){
case R.id.image:
Toast.makeText(MainActivity.this, "点击了图片", Toast.LENGTH_SHORT).show();
break;
case R.id.button_1:
Toast.makeText(MainActivity.this, "点击了按钮1", Toast.LENGTH_SHORT).show();
PhotoBean photoBean = list.get(position);
photoBean.setName("修改!");
photoBean.setUrl("http://pic29.nipic.com/20130511/9252150_174018365301_2.jpg");
adapter.notifyItemChanged(position);
break;
case R.id.button_2:
Toast.makeText(MainActivity.this, "点击了按钮2", Toast.LENGTH_SHORT).show();
break;
}
![](https://img.haomeiwen.com/i12648131/f1b5da56b27ceb6c.gif)
需求2:点击按钮2,使按钮2高亮。
首先添加一个clickButton状态
public class PhotoBean {
private String name;
private String url;
private byte clickButton;//0:点击了图片,1:点击了按钮1,2:点击了按钮2
public PhotoBean(){}
public PhotoBean(String url, String name, byte clickButton){
this.url = url;
this.name = name;
this.clickButton = clickButton;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public byte getClickButton() {
return clickButton;
}
public void setClickButton(byte clickButton) {
this.clickButton = clickButton;
}
}
修改onBindViewHolder方法,将按钮变黄
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
PhotoBean dataListBean = mData.get(position);
if(dataListBean != null){
Glide.with(mContext).load(dataListBean.getUrl()).into(((ViewHolder)holder).imgView);
((ViewHolder)holder).tvName.setText(dataListBean.getName());
if(dataListBean.getClickButton() == 2){
((ViewHolder)holder).button_2.setBackgroundColor(Color.YELLOW);
}
}
((ViewHolder)holder).imgView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mOnItemClickLitener != null) {
if(mData != null && mData.size() > 0){
mOnItemClickLitener.onItemClick(((ViewHolder)holder).imgView, position);
}
}
}
});
((ViewHolder)holder).button_1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mOnItemClickLitener != null) {
if(mData != null && mData.size() > 0){
mOnItemClickLitener.onItemClick(((ViewHolder)holder).button_1, position);
}
}
}
});
((ViewHolder)holder).button_2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mOnItemClickLitener != null) {
if(mData != null && mData.size() > 0){
mOnItemClickLitener.onItemClick(((ViewHolder)holder).button_2, position);
}
}
}
});
}
效果如下:
![](https://img.haomeiwen.com/i12648131/37c69338245b9282.gif)
这样貌似已经实现了功能,但是存在两个严重的弊端:
1.我只要将按钮2改变颜色就行了,为什么图片还会刷新?
2.我每次点击按钮2,图片、文字、按钮都要重新排版一次,严重消耗性能,也许现在手机性能比较好,很多人不在乎,但是我在乎。
解决方案:使用payloads实现刷新Item中的部分控件。
修改按钮2点击事件实现
case R.id.button_2:
Toast.makeText(MainActivity.this, "点击了按钮2", Toast.LENGTH_SHORT).show();
PhotoBean photoBean2 = new PhotoBean();
photoBean2.setClickButton((byte) 2);
adapter.notifyItemChanged(position, photoBean2);
break;
添加回调方法
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position, @NonNull List<Object> payloads) {
if(payloads != null && !payloads.isEmpty()){
if(payloads.get(0) instanceof PhotoBean){
PhotoBean photoBean = (PhotoBean) payloads.get(0);
if(photoBean.getClickButton() == 2){
((ViewHolder)holder).button_2.setBackgroundColor(Color.YELLOW);
}
}
}else{
onBindViewHolder(holder, position);
}
}
展示效果
![](https://img.haomeiwen.com/i12648131/26cc361114bce3d6.gif)
需求3:实现添加删除和移动功能
case R.id.button_3:
Toast.makeText(MainActivity.this, "添加Item", Toast.LENGTH_SHORT).show();
list.add(position, new PhotoBean(list.get(position).getUrl(), list.get(position).getName(), list.get(position).getClickButton()));
adapter.notifyItemInserted(position);
break;
case R.id.button_4:
Toast.makeText(MainActivity.this, "删除Item", Toast.LENGTH_SHORT).show();
list.remove(position);
adapter.notifyItemRemoved(position);
break;
case R.id.button_5:
Toast.makeText(MainActivity.this, "移动Item", Toast.LENGTH_SHORT).show();
Collections.swap(list,position,position+1);// 元素互换
adapter.notifyItemMoved(position, position + 1);
break;
添加效果:
![](https://img.haomeiwen.com/i12648131/84d357de629970da.gif)
删除效果:
![](https://img.haomeiwen.com/i12648131/3e172a54ef7fe62f.gif)
移动效果:
![](https://img.haomeiwen.com/i12648131/7588f33535427f0c.gif)
另外,以上讲解的都是刷新一个Item, 下面四个方法是更新多个Item
public final void notifyItemRangeChanged(int positionStart, int itemCount);
public final void notifyItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload);
public final void notifyItemRangeInserted(int positionStart, int itemCount);
public final void notifyItemRangeRemoved(int positionStart, int itemCount) ;
其实,更新一个Item的方法,底层都会调用以上四个方法的其中一个,只是此时itemCount=1。
可能会出现的紊乱问题:
Item紊乱是一个极其容易出现的问题,想要彻底解决这个问题请往下看:
方法一:
@Override
public int getItemViewType(int position) {
//防止滑动时混乱的问题
return position;
}
以上代码是为了防止Item滑动紊乱的问题,加上这句话可以基本解决Item紊乱的问题,原理是:
- 列表中所有Item的view类型都不同,可以保证在任何时候都能执行onBindViewHolder,进而更新数据;
方法二:
紊乱现象只是数据紊乱的假象,我们可以不要重写getItemViewType也可以解决紊乱的问题。
getItemViewType默认返回值是0, 也就是说默认情况下Item类型是一致的。
场景1:加载本地图片(或网络图片)
在onBindViewHolder中的写法如下
if(图片存在){
//加载已存在的图片
}
场景2:姓名展示
在onBindViewHolder中的写法如下
if(存在姓名){
//加载已存在的姓名
}
如果getItemViewType返回的是固定值时,这两种场景的代码会发生100%紊乱,解决方案如下:
场景1:加载本地图片(或网络图片)
在onBindViewHolder中的写法如下
if(图片存在){
//加载已存在的图片
}else{
//加载默认的图片
}
场景2:姓名展示
在onBindViewHolder中的写法如下
if(存在姓名){
//加载已存在的姓名
}else{
//加载默认的姓名
}
也就是说,如果getItemViewType返回的是固定值时,解决紊乱的问题必须从数据处理着手。
另外,上面所提到的插入:notifyItemInserted、删除:notifyItemRemoved、移动:notifyItemMoved,不使用方法一的话会出现紊乱,解决方案如下:
case R.id.button_3:
Toast.makeText(MainActivity.this, "添加Item", Toast.LENGTH_SHORT).show();
list.add(position, new PhotoBean(photoBean.getUrl(), photoBean.getName(), photoBean.getClickButton()));
adapter.notifyItemInserted(position);
if(position != list.size()){
adapter.notifyItemRangeChanged(position, list.size() - position);
}
break;
case R.id.button_4:
Toast.makeText(MainActivity.this, "删除Item", Toast.LENGTH_SHORT).show();
list.remove(position);
adapter.notifyItemRemoved(position);
if(position != list.size()){
adapter.notifyItemRangeChanged(position, list.size() - position);
}
break;
case R.id.button_5:
Toast.makeText(MainActivity.this, "移动Item", Toast.LENGTH_SHORT).show();
Collections.swap(list,position,position+1);// 元素互换
adapter.notifyItemMoved(position, position + 1);
if(position != list.size()){
adapter.notifyItemRangeChanged(position, list.size() - position);
}
也就是说,必须将可能会发生重新布局的地方刷新,这里考虑的性能问题就没有全部刷新,notifyItemRangeChanged这个方法是局部刷新的实现。
推荐:如果Item的类型单一,那么为了以防紊乱,推荐使用方法一
但是有人问了, 如果在某个场景下需要支持多类型的recycleview,在列表中每一种类型都有很多个Item,比如聊天消息,聊天消息可能会分为:文本、图片、语音、视频,如果列表中存在多个文本消息(相同类型的Item),那么只能采用方法二了。
网友评论