Android RecyclerView
作者:互联网
RecyclerView 常规使用
DataItem.java
public class DataItem {
public String id;
public String title;
public String subtitle;
public String content;
public int imageResId;
}
- 可选实现 Cloneable 接口,可解决后面遇到对象引用问题
listitem_image_text.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/id_tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:padding="10dp"
android:singleLine="true"
android:textSize="22sp"
app:layout_constraintEnd_toStartOf="@id/id_iv"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="标题标题标题标题标题标题标题标题标题标题end" />
<TextView
android:id="@+id/id_tv_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:padding="10dp"
android:singleLine="true"
android:textSize="18sp"
app:layout_constraintEnd_toStartOf="@id/id_iv"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/id_tv_title"
tools:text="副标题副标题副标题副标题副标题副标题副标题副标题副标题副标题副标题end" />
<TextView
android:id="@+id/id_tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:textSize="14sp"
app:layout_constraintEnd_toStartOf="@id/id_iv"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/id_tv_subtitle"
tools:text="内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容end" />
<!--在 id_tv_title,id_tv_subtitle,id_tv_content 三个控件右侧设置一道屏障-->
<androidx.constraintlayout.widget.Barrier
android:id="@+id/id_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="right"
app:constraint_referenced_ids="id_tv_title,id_tv_subtitle,id_tv_content" />
<!--id_iv 控件依赖上面这道屏障-->
<ImageView
android:id="@+id/id_iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/id_barrier" />
</androidx.constraintlayout.widget.ConstraintLayout>
DataItemViewHolder.java
public class DataItemViewHolder extends RecyclerView.ViewHolder {
TextView id_tv_title;
TextView id_tv_subtitle;
TextView id_tv_content;
ImageView id_iv;
public DataItemViewHolder(@NonNull View itemView) {
super(itemView);
id_tv_title = itemView.findViewById(R.id.id_tv_title);
id_tv_subtitle = itemView.findViewById(R.id.id_tv_subtitle);
id_tv_content = itemView.findViewById(R.id.id_tv_content);
id_iv = itemView.findViewById(R.id.id_iv);
}
}
DataItemRVAdapter.java
public class DataItemRVAdapter extends RecyclerView.Adapter<DataItemViewHolder> {
public static final String KEY_TITLE = "KEY_TITLE";
public static final String KEY_SUBTITLE = "KEY_SUBTITLE";
public static final String KEY_CONTENT = "KEY_CONTENT";
public static final String KEY_IMAGE = "KEY_IMAGE";
private List<DataItem> mDataItemList;
public void setDataItemList(List<DataItem> dataItemList) {
this.mDataItemList = new ArrayList<>(dataItemList);
/*this.mDataItemList = new ArrayList<>();
this.mDataItemList.addAll(dataItemList);*/
}
public List<DataItem> getDataItemList() {
return mDataItemList;
}
@NonNull
@Override
public DataItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.listitem_image_text, parent, false);
return new DataItemViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull DataItemViewHolder holder, int position) {
DataItem dataItem = mDataItemList.get(position);
holder.id_tv_title.setText(dataItem.title);
holder.id_tv_subtitle.setText(dataItem.subtitle);
holder.id_tv_content.setText(dataItem.content);
holder.id_iv.setImageResource(dataItem.imageResId);
}
@Override
public void onBindViewHolder(@NonNull DataItemViewHolder holder, int position, @NonNull List<Object> payloads) {
if (payloads.isEmpty()) {
//【列表刷新】 或者 【列表局部 Item 刷新】
super.onBindViewHolder(holder, position, payloads);
} else {
//【Item 局部 View 刷新】
Object payload = payloads.get(0);
if (payload instanceof Map) {
//【key-value 键值对】
Map<String, String> map = (Map<String, String>) payload;
for (Map.Entry<String, String> stringObjectEntry : map.entrySet()) {
String key = stringObjectEntry.getKey();
String value = stringObjectEntry.getValue();
//简单封装
bindViewHolder(holder, position, key, value);
}
} else if (payload instanceof String) {
//【key 标记】
String key = (String) payload;
//简单封装
bindViewHolder(holder, position, key, null);
}
}
}
private void bindViewHolder(DataItemViewHolder holder, int position, String key, String value) {
DataItem dataItem = mDataItemList.get(position);
if (key.equals(KEY_TITLE)) {
holder.id_tv_title.setText(value != null ? value : dataItem.title);
} else if (key.equals(KEY_SUBTITLE)) {
holder.id_tv_subtitle.setText(value != null ? value : dataItem.content);
} else if (key.equals(KEY_CONTENT)) {
holder.id_tv_content.setText(value != null ? value : dataItem.content);
} else if (key.equals(KEY_IMAGE)) {
holder.id_iv.setImageResource(value != null ? Integer.parseInt(value) : dataItem.imageResId);
}
}
@Override
public int getItemCount() {
return mDataItemList.size();
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
private List<DataItem> mDataItemList;
private DataItemRVAdapter mDataItemRVAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button id_btn = findViewById(R.id.id_btn);
id_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//
refreshData();
}
});
//
initData();
//
RecyclerView id_rv = findViewById(R.id.id_rv);
id_rv.setLayoutManager(new LinearLayoutManager(this));
id_rv.setItemAnimator(new DefaultItemAnimator());
mDataItemRVAdapter = new DataItemRVAdapter();
//初始化
id_rv.setAdapter(mDataItemRVAdapter);
mDataItemRVAdapter.setDataItemList(mDataItemList);
}
private void initData() {
mDataItemList = new ArrayList<>();
for (int i = 0; i < 15; i++) {
DataItem dataItem = new DataItem();
dataItem.id = String.valueOf(i + 1);
dataItem.title = dataItem.id + " title";
dataItem.subtitle = dataItem.id + " subtitle";
dataItem.content = dataItem.id + " content";
dataItem.imageResId = R.mipmap.ic_launcher;
mDataItemList.add(dataItem);
}
}
private void refreshData() {
//mDataItemList 存放最新的数据
//1 修改数据
//引用传递,会影响 Adapter 里 list(虽然 Adapter 里 list 已经是 new ArrayList 了)对应索引的对象的值,但是因为这里不影响
mDataItemList.get(1).title = "222 title new";
mDataItemList.get(1).subtitle = "222 subtitle new";
mDataItemList.get(1).content = "222 content new";
//2 更新 Adapter 数据源
mDataItemRVAdapter.setDataItemList(mDataItemList);
//3 通过 Adapter#notifyDataSetChanged 方式通知数据刷新
mDataItemRVAdapter.notifyDataSetChanged();
}
}
1 全局刷新
- 列表刷新
//列表可见部分全部刷新
mAdapter.notifyDataSetChanged()
2 局部刷新 Item 刷新
- Item 整体刷新
//修改指定 position 上 Item 的刷新
mAdapter.notifyItemChanged(int position)
//添加指定 position 上 Item 的刷新
mAdapter.notifyItemInserted(int position)
//移除指定 position 上 Item 的刷新
mAdapter.notifyItemRemoved(int position)
//修改从 positionStart 开始到 itemCount 数量的 Item 的刷新
mAdapter.notifyItemRangeChanged(int positionStart, int itemCount)
//添加从 positionStart 开始到 itemCount 数量的 Item 的刷新
mAdapter.notifyItemRangeInserted(int positionStart, int itemCount)
//移除从 positionStart 开始到 itemCount 数量的 Item 的刷新
mAdapter.notifyItemRangeRemoved(int positionStart, int itemCount)
//移到指定 fromPosition 到 toPosition 上 Item 的刷新
mAdapter.notifyItemMoved(int fromPosition, int toPosition)
3 局部刷新 View 刷新
- Item 的部分 View 控件刷新
//修改指定 position 上 Item 局部 View 的刷新,
mAdapter.notifyItemChanged(int position,Object payload)
//修改从 positionStart 开始到 itemCount 数量的 Item 局部 View 的刷新
mAdapter.notifyItemRangeChanged(int positionStart, int itemCount,Object payload)
- 假设 payload 只传 【key 标记】的话,刷新前的数据源就需要修改完毕,这样 Adapter 才直接用数据源
- 另外 payload 可以传 【key-value 键值对】,比如 Map ,那么 Adapter 可以不用数据源而改用 value
修改 DataItemRVAdapter
- 覆写带 Payload 参数的 onBindViewHolder 方法
@Override
public void onBindViewHolder(@NonNull DataItemViewHolder holder, int position, @NonNull List<Object> payloads) {
if (payloads.isEmpty()) {
//【列表刷新】 或者 【列表局部 Item 刷新】
super.onBindViewHolder(holder, position, payloads);
} else {
//【Item 局部 View 刷新】
Object payload = payloads.get(0);
if (payload instanceof Map) {
//【key-value 键值对】
Map<String, String> map = (Map<String, String>) payload;
for (Map.Entry<String, String> stringObjectEntry : map.entrySet()) {
String key = stringObjectEntry.getKey();
String value = stringObjectEntry.getValue();
//简单封装
bindViewHolder(holder, position, key, value);
}
} else if (payload instanceof String) {
//【key 标记】
String key = (String) payload;
//简单封装
bindViewHolder(holder, position, key, null);
}
}
}
private void bindViewHolder(DataItemViewHolder holder, int position, String key, String value) {
DataItem dataItem = mDataItemList.get(position);
if (key.equals(KEY_TITLE)) {
holder.id_tv_title.setText(value != null ? value : dataItem.title);
} else if (key.equals(KEY_SUBTITLE)) {
holder.id_tv_subtitle.setText(value != null ? value : dataItem.content);
} else if (key.equals(KEY_CONTENT)) {
holder.id_tv_content.setText(value != null ? value : dataItem.content);
} else if (key.equals(KEY_IMAGE)) {
holder.id_iv.setImageResource(value != null ? Integer.parseInt(value) : dataItem.imageResId);
}
}
使用 DiffUtil 刷新数据
1 局部刷新 Item 刷新
- Item 整体刷新
创建 DiffUtilCallback
public class DiffUtilCallback extends DiffUtil.Callback {
private List<DataItem> mOldDataItemList;
private List<DataItem> mNewDataItemList;
public DiffCallback(List<DataItem> oldDataItemList, List<DataItem> newDataItemList) {
this.mOldDataItemList = oldDataItemList;
this.mNewDataItemList = newDataItemList;
}
@Override
public int getOldListSize() {
return mOldDataItemList != null ? mOldDataItemList.size() : 0;
}
@Override
public int getNewListSize() {
return mNewDataItemList != null ? mNewDataItemList.size() : 0;
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
//判断是不是同一个 item 的逻辑
DataItem oldDataItem = mOldDataItemList.get(oldItemPosition);
DataItem newDataItem = mNewDataItemList.get(newItemPosition);
return Objects.equals(oldDataItem.id, newDataItem.id);
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
//areItemsTheSame 返回 true 的情况下走这个方法,进一步判断这个 item 的内容是否有变化
DataItem oldDataItem = mOldDataItemList.get(oldItemPosition);
DataItem newDataItem = mNewDataItemList.get(newItemPosition);
// id
if (!Objects.equals(oldDataItem.id, newDataItem.id)) {
return false;
}
// name
if (!Objects.equals(oldDataItem.name, newDataItem.name)) {
return false;
}
// content
if (!Objects.equals(oldDataItem.content, newDataItem.content)) {
return false;
}
//如果还有,继续写其他判断条件
//默认返回内容相等
return true;
}
}
修改 MainActivity 使用
private void refreshDataByDiffUtil() {
List<DataItem> oldDataItemList = new ArrayList<>(mDataItemRVAdapter.getDataItemList());
//mDataItemList 存放最新的数据
//1 修改数据
//别这么写!!!引用传递 引用传递会导致 calculateDiff 失效
// mDataItemList.get(1).name = "222 new";
DataItem oldDataItem = mDataItemList.get(2);
DataItem newDataItem = new DataItem();
newDataItem.id = oldDataItem.id;
newDataItem.name = "333 new";//就改一个字段
newDataItem.content = oldDataItem.content;
mDataItemList.set(2, newDataItem);
//2 更新 Adapter 数据源
mDataItemRVAdapter.setDataItemList(mDataItemList);
//3 DiffUtil 方式通知刷新
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallback(oldDataItemList, mDataItemList));
//内部最终就是调用 Adapter#notifyItemXXX 系列方法实现的
diffResult.dispatchUpdatesTo(mDataItemRVAdapter);
}
- 能够保留了 item 动画
2 局部刷新 View 刷新
-
Item 的部分 View 控件刷新
-
同上,Adapter 需要覆写带 Payload 参数的 onBindViewHolder 方法
修改 DiffUtilCallback
- 覆写 getChangePayload 方法
@Nullable
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
DataItem oldDataItem = mOldDataItemList.get(oldItemPosition);
DataItem newDataItem = mNewDataItemList.get(newItemPosition);
// areItemsTheSame 返回 true 并且 areContentsTheSame 返回 false 的情况才会进来
Map<String, String> payloadMap = new HashMap<>();
// title
if (!Objects.equals(oldDataItem.title, newDataItem.title)) {
payloadMap.put(DataItemRVAdapter.KEY_TITLE, newDataItem.title);
}
// title
if (!Objects.equals(oldDataItem.subtitle, newDataItem.subtitle)) {
payloadMap.put(DataItemRVAdapter.KEY_SUBTITLE, newDataItem.subtitle);
}
// content
if (!Objects.equals(oldDataItem.content, newDataItem.content)) {
payloadMap.put(DataItemRVAdapter.KEY_CONTENT, newDataItem.content);
}
// imageResId
if (oldDataItem.imageResId != newDataItem.imageResId) {
payloadMap.put(DataItemRVAdapter.KEY_IMAGE, String.valueOf(newDataItem.imageResId));
}
if (!payloadMap.isEmpty()) {
return payloadMap;
}
//默认无变化,内部传的是 null
return super.getChangePayload(oldItemPosition, newItemPosition);
}
使用
- 同上
标签:dataItem,int,value,public,content,Android,RecyclerView,id 来源: https://blog.csdn.net/louisgeek/article/details/116172277