Android DataBinding与RecyclerView深度集成:列表数据绑定的源码级剖析
一、引言
在现代Android开发中,RecyclerView已成为展示列表数据的标准组件,而DataBinding则为视图与数据之间的绑定提供了高效的解决方案。当这两者结合使用时,可以极大地简化列表数据的展示和更新逻辑。本文将从源码级别深入分析Android DataBinding与RecyclerView结合使用的原理与实现细节,通过大量实例代码和详细注释,全面解析其实现机制。
二、RecyclerView与DataBinding基础
2.1 RecyclerView概述
RecyclerView是Android提供的一个强大的列表控件,用于高效展示大量数据。
// RecyclerView的基本使用
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化RecyclerView
recyclerView = findViewById(R.id.recyclerView);
// 设置布局管理器
recyclerView.setLayoutManager(new LinearLayoutManager(this));
// 设置适配器
adapter = new MyAdapter();
recyclerView.setAdapter(adapter);
// 设置数据
List<String> data = new ArrayList<>();
for (int i = 0; i < 100; i++) {
data.add("Item " + i);
}
adapter.setData(data);
}
}
2.2 DataBinding概述
DataBinding是Android官方提供的一个支持库,允许开发者将布局文件中的视图与应用程序中的数据源绑定。
// 启用DataBinding的build.gradle配置
android {
...
dataBinding {
enabled = true
}
}
2.3 简单的数据绑定示例
下面是一个简单的数据绑定示例,展示如何将数据绑定到TextView。
<!-- 布局文件 -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.example.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}" /> <!-- 绑定到user对象的name属性 -->
</LinearLayout>
</layout>
// User类
public class User extends BaseObservable {
private String name;
// 构造方法
public User(String name) {
this.name = name;
}
// 获取name属性,添加@Bindable注解以便DataBinding识别
@Bindable
public String getName() {
return name;
}
// 设置name属性,并通知属性变化
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name); // 通知name属性已变化
}
}
三、RecyclerView与DataBinding的基本集成
3.1 传统方式集成RecyclerView与DataBinding
下面是一个传统方式集成RecyclerView与DataBinding的示例。
// 使用DataBinding的RecyclerView适配器
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private List<String> data = new ArrayList<>();
// 设置数据
public void setData(List<String> data) {
this.data = data;
notifyDataSetChanged(); // 通知适配器数据已变化
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// 使用DataBindingUtil.inflate方法创建Item布局的绑定类
ItemLayoutBinding binding = ItemLayoutBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false);
return new MyViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
// 获取当前位置的数据
String item = data.get(position);
// 设置数据到绑定类
holder.binding.setItem(item);
// 执行挂起的绑定,确保数据立即更新到视图
holder.binding.executePendingBindings();
}
@Override
public int getItemCount() {
return data.size();
}
// ViewHolder类,持有绑定类的引用
public static class MyViewHolder extends RecyclerView.ViewHolder {
private ItemLayoutBinding binding;
public MyViewHolder(@NonNull ItemLayoutBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}
<!-- 列表项布局文件 -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="item"
type="java.lang.String" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{item}" /> <!-- 绑定到item变量 -->
</LinearLayout>
</layout>
3.2 使用DataBinding的RecyclerView适配器
下面是一个更通用的RecyclerView适配器,使用DataBinding处理不同类型的列表项。
// 通用的RecyclerView适配器,使用DataBinding
public class GenericAdapter<T> extends RecyclerView.Adapter<GenericAdapter.BindingViewHolder> {
private List<T> items = new ArrayList<>();
private int layoutId;
private int variableId;
// 构造方法,传入布局ID和变量ID
public GenericAdapter(int layoutId, int variableId) {
this.layoutId = layoutId;
this.variableId = variableId;
}
// 设置数据
public void setItems(List<T> items) {
this.items = items;
notifyDataSetChanged(); // 通知适配器数据已变化
}
@NonNull
@Override
public BindingViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// 使用DataBindingUtil.inflate方法创建布局的绑定类
ViewDataBinding binding = DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()), layoutId, parent, false);
return new BindingViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull BindingViewHolder holder, int position) {
// 获取当前位置的数据
T item = items.get(position);
// 设置变量到绑定类
holder.binding.setVariable(variableId, item);
// 执行挂起的绑定,确保数据立即更新到视图
holder.binding.executePendingBindings();
}
@Override
public int getItemCount() {
return items.size();
}
// ViewHolder类,持有绑定类的引用
public static class BindingViewHolder extends RecyclerView.ViewHolder {
private ViewDataBinding binding;
public BindingViewHolder(@NonNull ViewDataBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}
// 在Activity中使用通用适配器
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private GenericAdapter<String> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化RecyclerView
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
// 创建适配器,传入布局ID和变量ID
adapter = new GenericAdapter<>(R.layout.item_layout, BR.item);
// 设置适配器
recyclerView.setAdapter(adapter);
// 设置数据
List<String> data = new ArrayList<>();
for (int i = 0; i < 100; i++) {
data.add("Item " + i);
}
adapter.setItems(data);
}
}
四、编译时处理
4.1 布局文件解析
在编译时,DataBinding框架会解析布局文件,提取数据绑定表达式。
// 布局文件解析器的简化实现
public class LayoutFileParser {
// 解析布局文件,提取数据绑定信息
public BindingInfo parseLayoutFile(File layoutFile) throws XmlPullParserException, IOException {
BindingInfo bindingInfo = new BindingInfo();
// 创建XML解析器
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new FileInputStream(layoutFile), null);
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
// 处理标签
if (parser.getName().equals("layout")) {
// 处理layout标签
parseLayoutTag(parser, bindingInfo);
} else if (parser.getName().equals("data")) {
// 处理data标签
parseDataTag(parser, bindingInfo);
} else {
// 处理普通视图标签
parseViewTag(parser, bindingInfo);
}
}
eventType = parser.next();
}
return bindingInfo;
}
// 解析layout标签
private void parseLayoutTag(XmlPullParser parser, BindingInfo bindingInfo) {
// 处理layout标签的属性
for (int i = 0; i < parser.getAttributeCount(); i++) {
String attrName = parser.getAttributeName(i);
String attrValue = parser.getAttributeValue(i);
// 处理属性...
}
}
// 解析data标签
private void parseDataTag(XmlPullParser parser, BindingInfo bindingInfo) throws XmlPullParserException, IOException {
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_TAG || !parser.getName().equals("data")) {
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals("variable")) {
// 解析variable标签
parseVariableTag(parser, bindingInfo);
} else if (parser.getName().equals("import")) {
// 解析import标签
parseImportTag(parser, bindingInfo);
}
}
eventType = parser.next();
}
}
// 解析variable标签
private void parseVariableTag(XmlPullParser parser, BindingInfo bindingInfo) {
String name = parser.getAttributeValue(null, "name");
String type = parser.getAttributeValue(null, "type");
// 创建变量信息对象
VariableInfo variableInfo = new VariableInfo();
variableInfo.setName(name);
variableInfo.setType(type);
// 添加到绑定信息中
bindingInfo.addVariable(variableInfo);
}
// 解析import标签
private void parseImportTag(XmlPullParser parser, BindingInfo bindingInfo) {
String type = parser.getAttributeValue(null, "type");
String alias = parser.getAttributeValue(null, "alias");
// 创建导入信息对象
ImportInfo importInfo = new ImportInfo();
importInfo.setType(type);
importInfo.setAlias(alias);
// 添加到绑定信息中
bindingInfo.addImport(importInfo);
}
// 解析普通视图标签
private void parseViewTag(XmlPullParser parser, BindingInfo bindingInfo) {
String tagName = parser.getName();
int id = parser.getIdAttributeResourceValue(-1);
// 创建视图信息对象
ViewInfo viewInfo = new ViewInfo();
viewInfo.setTagName(tagName);
viewInfo.setId(id);
// 处理视图的属性
for (int i = 0; i < parser.getAttributeCount(); i++) {
String attrNamespace = parser.getAttributeNamespace(i);
String attrName = parser.getAttributeName(i);
String attrValue = parser.getAttributeValue(i);
// 检查是否是数据绑定表达式
if (attrValue.startsWith("@{") && attrValue.endsWith("}")) {
// 提取表达式内容
String expression = attrValue.substring(2, attrValue.length() - 1);
// 创建属性绑定信息
AttributeBinding attributeBinding = new AttributeBinding();
attributeBinding.setNamespace(attrNamespace);
attributeBinding.setName(attrName);
attributeBinding.setExpression(expression);
// 添加到视图信息中
viewInfo.addAttributeBinding(attributeBinding);
}
}
// 添加到绑定信息中
bindingInfo.addView(viewInfo);
}
}
4.2 绑定类生成
DataBinding框架会为每个布局文件生成一个绑定类,负责处理数据与视图的绑定。
// 绑定类生成器的简化实现
public class BindingClassGenerator {
// 生成绑定类
public void generateBindingClass(BindingInfo bindingInfo, File outputDir) throws IOException {
String packageName = bindingInfo.getPackageName();
String className = bindingInfo.getClassName();
// 创建Java文件
File javaFile = new File(outputDir, className.replace('.', '/') + ".java");
javaFile.getParentFile().mkdirs();
javaFile.createNewFile();
// 创建输出流
try (BufferedWriter writer = new BufferedWriter(new FileWriter(javaFile))) {
// 写入包声明
writer.write("package " + packageName + ";\n\n");
// 写入导入语句
writer.write("import android.view.View;\n");
writer.write("import androidx.databinding.Bindable;\n");
writer.write("import androidx.databinding.DataBindingUtil;\n");
writer.write("import androidx.databinding.ViewDataBinding;\n");
// 写入布局文件中声明的导入
for (ImportInfo importInfo : bindingInfo.getImports()) {
writer.write("import " + importInfo.getType() + ";\n");
}
writer.write("\n");
// 写入类声明
writer.write("public class " + className + " extends ViewDataBinding {\n\n");
// 写入成员变量
writer.write(" @NonNull\n");
writer.write(" private final View rootView;\n\n");
// 写入变量引用
for (VariableInfo variableInfo : bindingInfo.getVariables()) {
writer.write(" @Nullable\n");
writer.write(" private " + variableInfo.getType() + " " +
variableInfo.getName() + ";\n");
}
writer.write("\n");
// 写入构造方法
writer.write(" public " + className + "(@NonNull View root) {\n");
writer.write(" super(root);\n");
writer.write(" this.rootView = root;\n");
// 初始化视图引用
for (ViewInfo viewInfo : bindingInfo.getViews()) {
writer.write(" this." + viewInfo.getIdName() + " = findViewById(root, " +
viewInfo.getId() + ");\n");
}
writer.write(" }\n\n");
// 写入设置变量的方法
writer.write(" @Override\n");
writer.write(" public boolean setVariable(int variableId, @Nullable Object variable) {\n");
writer.write(" switch (variableId) {\n");
for (VariableInfo variableInfo : bindingInfo.getVariables()) {
writer.write(" case " + getVariableId(variableInfo) + ":\n");
writer.write(" this." + variableInfo.getName() + " = (" +
variableInfo.getType() + ") variable;\n");
writer.write(" return true;\n");
}
writer.write(" default:\n");
writer.write(" return false;\n");
writer.write(" }\n");
writer.write(" }\n\n");
// 为每个变量写入单独的setter方法
for (VariableInfo variableInfo : bindingInfo.getVariables()) {
writer.write(" public void set" + capitalize(variableInfo.getName()) +
"(@Nullable " + variableInfo.getType() + " " + variableInfo.getName() + ") {\n");
writer.write(" this." + variableInfo.getName() + " = " + variableInfo.getName() + ";\n");
writer.write(" invalidateAll();\n");
writer.write(" }\n\n");
}
// 写入执行绑定的方法
writer.write(" @Override\n");
writer.write(" protected void executeBindings() {\n");
// 获取所有变量
for (VariableInfo variableInfo : bindingInfo.getVariables()) {
writer.write(" " + variableInfo.getType() + " " + variableInfo.getName() + "Value = " +
variableInfo.getName() + ";\n");
}
writer.write("\n");
// 处理每个视图的绑定
for (ViewInfo viewInfo : bindingInfo.getViews()) {
for (AttributeBinding attributeBinding : viewInfo.getAttributeBindings()) {
// 生成绑定代码
writer.write(" // " + viewInfo.getIdName() + "." + attributeBinding.getName() + "\n");
writer.write(" " + generateBindingCode(viewInfo, attributeBinding) + "\n");
}
}
writer.write(" }\n\n");
// 写入结束括号
writer.write("}\n");
}
}
// 生成绑定代码
private String generateBindingCode(ViewInfo viewInfo, AttributeBinding attributeBinding) {
// 简化实现:在实际代码中,这里会生成更复杂的绑定代码
return "// Binding code for " + viewInfo.getIdName() + "." + attributeBinding.getName();
}
// 获取变量ID
private String getVariableId(VariableInfo variableInfo) {
// 简化实现:在实际代码中,这里会使用BR类中的常量
return "BR." + variableInfo.getName();
}
// 首字母大写
private String capitalize(String s) {
if (s == null || s.isEmpty()) {
return s;
}
return Character.toUpperCase(s.charAt(0)) + s.substring(1);
}
}
五、运行时处理
5.1 绑定类初始化
在运行时,绑定类会被实例化并与布局视图关联。
// 绑定类初始化过程
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 使用DataBindingUtil.inflate方法创建绑定类实例
binding = DataBindingUtil.inflate(
getLayoutInflater(),
R.layout.activity_main,
null,
false);
// 设置ContentView为绑定类的根视图
setContentView(binding.getRoot());
// 初始化RecyclerView
RecyclerView recyclerView = binding.recyclerView;
recyclerView.setLayoutManager(new LinearLayoutManager(this));
// 创建适配器
MyAdapter adapter = new MyAdapter();
recyclerView.setAdapter(adapter);
// 设置数据
List<String> data = new ArrayList<>();
for (int i = 0; i < 100; i++) {
data.add("Item " + i);
}
adapter.setData(data);
}
}
5.2 数据变化监听与通知
当数据发生变化时,会触发通知机制。
// 数据变化通知流程
public class User extends BaseObservable {
private String name;
private int age;
// 构造方法
public User(String name, int age) {
this.name = name;
this.age = age;
}
// 获取name属性
@Bindable
public String getName() {
return name;
}
// 设置name属性,并通知变化
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name); // 通知name属性已变化
}
// 获取age属性
@Bindable
public int getAge() {
return age;
}
// 设置age属性,并通知变化
public void setAge(int age) {
this.age = age;
notifyPropertyChanged(BR.age); // 通知age属性已变化
}
}
5.3 UI更新过程
当数据变化通知到达时,绑定类会更新对应的UI组件。
// UI更新过程
public class ItemLayoutBindingImpl extends ItemLayoutBinding {
@NonNull
private final LinearLayout rootView;
@NonNull
public final TextView textView;
@Nullable
private String mItem; // 数据源对象
// 构造方法
public ItemLayoutBindingImpl(@NonNull View root) {
super(root);
this.rootView = (LinearLayout) root;
this.textView = findViewById(root, R.id.textView);
}
// 设置Item对象
public void setItem(@Nullable String item) {
mItem = item;
invalidateAll(); // 标记所有绑定需要重新计算
}
// 执行绑定操作
@Override
protected void executeBindings() {
String itemValue = mItem;
// 更新视图
textView.setText(itemValue);
}
}
六、与LiveData的集成
6.1 LiveData概述
LiveData是一种可观察的数据持有者类,它遵循应用程序组件的生命周期,确保仅在活动的生命周期状态下更新UI。
// LiveData类定义
public abstract class LiveData<T> {
// 添加观察者
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
// 检查生命周期状态
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// 已经销毁,直接返回
return;
}
// 创建包装后的观察者
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
// 存储观察者
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
// 处理已存在的观察者
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
// 绑定到生命周期
owner.getLifecycle().addObserver(wrapper);
}
// 设置值(主线程)
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
// 发布值(后台线程)
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
// 分发值给观察者
private void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
// 考虑通知观察者
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
// 检查生命周期状态
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
// 检查版本
if (observer.mLastVersion >= mVersion) {
return;
}
// 更新版本
observer.mLastVersion = mVersion;
// 调用观察者的onChanged方法
observer.mObserver.onChanged((T) mData);
}
// 观察者包装类
private abstract class ObserverWrapper {
final Observer<? super T> mObserver;
boolean mActive;
int mLastVersion = START_VERSION;
ObserverWrapper(Observer<? super T> observer) {
mObserver = observer;
}
abstract boolean shouldBeActive();
boolean isAttachedTo(LifecycleOwner owner) {
return false;
}
void detachObserver() {
}
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// 更改活动状态
mActive = newActive;
// 计算活动观察者数量
boolean wasInactive = LiveData.this.mActiveCount == 0;
LiveData.this.mActiveCount += mActive ? 1 : -1;
// 处理首次活动和不再活动的情况
if (wasInactive && mActive) {
onActive();
}
if (LiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
if (mActive) {
dispatchingValue(this);
}
}
}
// 生命周期绑定的观察者
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}
@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}
}
6.2 DataBinding与LiveData的集成
DataBinding可以直接与LiveData集成,实现自动更新UI。
// ViewModel类
public class UserViewModel extends ViewModel {
private MutableLiveData<List<User>> userListLiveData = new MutableLiveData<>();
public UserViewModel() {
// 初始化用户列表
List<User> userList = new ArrayList<>();
userList.add(new User("John Doe", 30));
userList.add(new User("Jane Smith", 25));
userList.add(new User("Bob Johnson", 35));
userListLiveData.setValue(userList);
}
// 获取用户列表LiveData
public LiveData<List<User>> getUserList() {
return userListLiveData;
}
// 添加新用户
public void addUser() {
List<User> currentList = userListLiveData.getValue();
List<User> newList = new ArrayList<>(currentList);
newList.add(new User("New User", 20));
userListLiveData.setValue(newList);
}
}
<!-- 布局文件 -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="com.example.UserViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:adapter="@{viewModel.userList}" /> <!-- 绑定到LiveData中的userList -->
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add User"
android:onClick="@{() -> viewModel.addUser()}" />
</LinearLayout>
</layout>
// Activity类
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private UserViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 创建ViewModel
viewModel = new ViewModelProvider(this).get(UserViewModel.class);
// 创建DataBinding
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
// 设置ViewModel到DataBinding
binding.setViewModel(viewModel);
// 设置生命周期所有者,使DataBinding能够观察LiveData
binding.setLifecycleOwner(this);
// 初始化RecyclerView
RecyclerView recyclerView = binding.recyclerView;
recyclerView.setLayoutManager(new LinearLayoutManager(this));
// 创建适配器
UserAdapter adapter = new UserAdapter();
recyclerView.setAdapter(adapter);
// 观察用户列表变化
viewModel.getUserList().observe(this, new Observer<List<User>>() {
@Override
public void onChanged(List<User> users) {
adapter.setUsers(users); // 更新适配器数据
}
});
}
}
6.3 LiveData到RecyclerView的更新流程
当LiveData的值发生变化时,会触发RecyclerView的更新。
// LiveData到RecyclerView的更新流程
public class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserViewHolder> {
private List<User> users = new ArrayList<>();
// 设置用户列表
public void setUsers(List<User> users) {
this.users = users;
notifyDataSetChanged(); // 通知适配器数据已变化
}
@NonNull
@Override
public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// 使用DataBindingUtil.inflate方法创建Item布局的绑定类
ItemUserBinding binding = ItemUserBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false);
return new UserViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
// 获取当前位置的用户
User user = users.get(position);
// 设置用户到绑定类
holder.binding.setUser(user);
// 执行挂起的绑定,确保数据立即更新到视图
holder.binding.executePendingBindings();
}
@Override
public int getItemCount() {
return users.size();
}
// ViewHolder类
public static class UserViewHolder extends RecyclerView.ViewHolder {
private ItemUserBinding binding;
public UserViewHolder(@NonNull ItemUserBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}
七、使用DiffUtil优化RecyclerView更新
7.1 DiffUtil概述
DiffUtil是Android提供的一个工具类,用于计算两个数据集之间的差异。
// DiffUtil类定义
public class DiffUtil {
// 计算两个数据集之间的差异
public static DiffResult calculateDiff(Callback callback) {
return calculateDiff(callback, false);
}
// 计算两个数据集之间的差异
public static DiffResult calculateDiff(Callback callback, boolean detectMoves) {
// 获取旧数据集的大小
final int oldSize = callback.getOldListSize();
// 获取新数据集的大小
final int newSize = callback.getNewListSize();
// 创建结果对象
final DiffResult result = new DiffResult(callback, detectMoves);
// 处理特殊情况
if (oldSize == 0) {
// 旧数据集为空,所有项都是新增的
result.mNewItemCount = newSize;
result.mOldItemCount = 0;
for (int i = 0; i < newSize; i++) {
result.mPostponedUpdates.add(new UpdateOp(UpdateOp.ADD, -1, i));
}
return result;
}
if (newSize == 0) {
// 新数据集为空,所有项都是删除的
result.mNewItemCount = 0;
result.mOldItemCount = oldSize;
for (int i = 0; i < oldSize; i++) {
result.mPostponedUpdates.add(new UpdateOp(UpdateOp.REMOVE, i, -1));
}
return result;
}
// 使用 Myers 差异算法计算差异
final List<Snake> snakes = new ArrayList<>();
final SparseIntArray forward = new SparseIntArray();
final SparseIntArray backward = new SparseIntArray();
// 初始化
int max = oldSize + newSize + 1;
forward.put(1, 0);
backward.put(1, oldSize);
// 执行算法
int d;
for (d = 0; d <= (oldSize + newSize + 1) / 2; d++) {
// 向前搜索
for (int k = -d; k <= d; k += 2) {
int x;
if (k == -d || (k != d && forward.get(k - 1) < forward.get(k + 1))) {
x = forward.get(k + 1);
} else {
x = forward.get(k - 1) + 1;
}
int y = x - k;
while (x < oldSize && y < newSize && callback.areItemsTheSame(x, y)) {
x++;
y++;
}
forward.put(k, x);
if (x >= oldSize && y >= newSize) {
// 找到解
result.mNewItemCount = newSize;
result.mOldItemCount = oldSize;
result.mSnakes = snakes;
result.mReverse = false;
return result;
}
}
// 向后搜索
for (int k = -d; k <= d; k += 2) {
int x;
if (k == -d || (k != d && backward.get(k - 1) < backward.get(k + 1))) {
x = backward.get(k + 1);
} else {
x = backward.get(k - 1) + 1;
}
int y = x - k;
while (x > 0 && y > 0 && callback.areItemsTheSame(x - 1, y - 1)) {
x--;
y--;
}
backward.put(k, x);
if (x <= 0 && y <= 0) {
// 找到解
result.mNewItemCount = newSize;
result.mOldItemCount = oldSize;
result.mSnakes = snakes;
result.mReverse = true;
return result;
}
}
}
throw new IllegalStateException("DiffUtil hit an unexpected case while trying to calculate"
+ " the optimal path. Please make sure your data is not changing during the"
+ " diff calculation.");
}
// 回调接口,用于比较两个数据集
public abstract static class Callback {
// 获取旧数据集的大小
public abstract int getOldListSize();
// 获取新数据集的大小
public abstract int getNewListSize();
// 判断两个位置的项是否代表同一个对象
public abstract boolean areItemsTheSame(int oldItemPosition, int newItemPosition);
// 判断两个项的内容是否相同
public abstract boolean areContentsTheSame(int oldItemPosition, int newItemPosition);
// 获取变化的有效负载
@Nullable
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
return null;
}
}
// 差异结果类
public static class DiffResult {
// 各种更新操作类型
public static final int UPDATE = 0;
public static final int INSERT = 1;
public static final int REMOVE = 2;
public static final int MOVE = 3;
// 其他成员变量和方法...
}
}
7.2 使用DiffUtil优化RecyclerView适配器
下面是一个使用DiffUtil优化的RecyclerView适配器。
// 使用DiffUtil优化的RecyclerView适配器
public class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserViewHolder> {
private List<User> users = new ArrayList<>();
// 设置用户列表
public void setUsers(List<User> newUsers) {
// 计算差异
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new UserDiffCallback(users, newUsers));
// 更新数据
users.clear();
users.addAll(newUsers);
// 应用差异
diffResult.dispatchUpdatesTo(this);
}
@NonNull
@Override
public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// 使用DataBindingUtil.inflate方法创建Item布局的绑定类
ItemUserBinding binding = ItemUserBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false);
return new UserViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
// 获取当前位置的用户
User user = users.get(position);
// 设置用户到绑定类
holder.binding.setUser(user);
// 执行挂起的绑定,确保数据立即更新到视图
holder.binding.executePendingBindings();
}
@Override
public int getItemCount() {
return users.size();
}
// DiffUtil回调类
private static class UserDiffCallback extends DiffUtil.Callback {
private List<User> oldUsers;
private List<User> newUsers;
public UserDiffCallback(List<User> oldUsers, List<User> newUsers) {
this.oldUsers = oldUsers;
this.newUsers = newUsers;
}
@Override
public int getOldListSize() {
return oldUsers.size();
}
@Override
public int getNewListSize() {
return newUsers.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
// 判断是否是同一个用户(通过ID比较)
return oldUsers.get(oldItemPosition).getId() == newUsers.get(newItemPosition).getId();
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
// 判断两个用户的内容是否相同
User oldUser = oldUsers.get(oldItemPosition);
User newUser = newUsers.get(newItemPosition);
return oldUser.getName().equals(newUser.getName()) &&
oldUser.getAge() == newUser.getAge();
}
@Nullable
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
// 获取变化的有效负载
User oldUser = oldUsers.get(oldItemPosition);
User newUser = newUsers.get(newItemPosition);
Bundle diffBundle = new Bundle();
if (!oldUser.getName().equals(newUser.getName())) {
diffBundle.putString("name", newUser.getName());
}
if (oldUser.getAge() != newUser.getAge()) {
diffBundle.putInt("age", newUser.getAge());
}
if (diffBundle.size() == 0) {
return null;
}
return diffBundle;
}
}
// ViewHolder类
public static class UserViewHolder extends RecyclerView.ViewHolder {
private ItemUserBinding binding;
public UserViewHolder(@NonNull ItemUserBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}
7.3 使用Payloads优化部分更新
可以使用payloads来优化只更新发生变化的部分UI。
// 使用payloads优化部分更新
@Override
public void onBindViewHolder(@NonNull UserViewHolder holder, int position, @NonNull List<Object> payloads) {
if (payloads.isEmpty()) {
// 没有有效负载,进行完整绑定
onBindViewHolder(holder, position);
} else {
// 有有效负载,只更新变化的部分
Bundle payload = (Bundle) payloads.get(0);
User user = users.get(position);
if (payload.containsKey("name")) {
holder.binding.textViewName.setText(payload.getString("name"));
}
if (payload.containsKey("age")) {
holder.binding.textViewAge.setText(String.valueOf(payload.getInt("age")));
}
}
}
八、使用ListAdapter简化实现
8.1 ListAdapter概述
ListAdapter是RecyclerView.Adapter的抽象子类,内置了DiffUtil功能,可以简化RecyclerView适配器的实现。
// ListAdapter类定义
public abstract class ListAdapter<T, VH extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<VH> {
private final AsyncListDiffer<T> mDiffer;
private final DiffUtil.ItemCallback<T> mDiffCallback;
// 构造方法
protected ListAdapter(@NonNull DiffUtil.ItemCallback<T> diffCallback) {
mDiffCallback = diffCallback;
mDiffer = new AsyncListDiffer<>(
new AdapterListUpdateCallback(this),
new AsyncDifferConfig.Builder<T>(diffCallback).build());
}
// 构造方法
protected ListAdapter(@NonNull Executor mainThreadExecutor,
@NonNull Executor backgroundExecutor,
@NonNull DiffUtil.ItemCallback<T> diffCallback) {
mDiffCallback = diffCallback;
mDiffer = new AsyncListDiffer<>(
new AdapterListUpdateCallback(this),
new AsyncDifferConfig.Builder<T>(diffCallback)
.setMainThreadExecutor(mainThreadExecutor)
.setBackgroundExecutor(backgroundExecutor)
.build());
}
// 设置新的列表数据
public void submitList(@Nullable List<T> list) {
mDiffer.submitList(list);
}
// 设置新的列表数据,并提供一个回调,当
8.1 ListAdapter概述(续)
// 设置新的列表数据,并提供一个回调,当差异计算完成且更新已发布时执行
public void submitList(@Nullable List<T> list, @Nullable Runnable commitCallback) {
mDiffer.submitList(list, commitCallback);
}
// 获取当前列表数据
@Nullable
public T getItem(int position) {
return mDiffer.getCurrentList().get(position);
}
// 获取当前列表大小
@Override
public int getItemCount() {
return mDiffer.getCurrentList().size();
}
// 获取差异回调
@NonNull
protected DiffUtil.ItemCallback<T> getDiffCallback() {
return mDiffCallback;
}
}
8.2 使用ListAdapter实现RecyclerView适配器
下面是一个使用ListAdapter实现的RecyclerView适配器示例。
// 使用ListAdapter的RecyclerView适配器
public class UserAdapter extends ListAdapter<User, UserAdapter.UserViewHolder> {
// 创建DiffUtil.ItemCallback实例
private static final DiffUtil.ItemCallback<User> DIFF_CALLBACK = new DiffUtil.ItemCallback<User>() {
@Override
public boolean areItemsTheSame(@NonNull User oldItem, @NonNull User newItem) {
// 判断是否是同一个用户(通过ID比较)
return oldItem.getId() == newItem.getId();
}
@Override
public boolean areContentsTheSame(@NonNull User oldItem, @NonNull User newItem) {
// 判断两个用户的内容是否相同
return oldItem.getName().equals(newItem.getName()) &&
oldItem.getAge() == newItem.getAge();
}
};
// 构造方法
public UserAdapter() {
super(DIFF_CALLBACK);
}
@NonNull
@Override
public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// 使用DataBindingUtil.inflate方法创建Item布局的绑定类
ItemUserBinding binding = ItemUserBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false);
return new UserViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
// 获取当前位置的用户
User user = getItem(position);
// 设置用户到绑定类
holder.binding.setUser(user);
// 执行挂起的绑定,确保数据立即更新到视图
holder.binding.executePendingBindings();
}
// ViewHolder类
public static class UserViewHolder extends RecyclerView.ViewHolder {
private ItemUserBinding binding;
public UserViewHolder(@NonNull ItemUserBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}
8.3 在ViewModel中使用ListAdapter
下面是一个在ViewModel中使用ListAdapter的示例。
// 在ViewModel中使用ListAdapter
public class UserViewModel extends ViewModel {
private MutableLiveData<PagedList<User>> userListLiveData;
private UserAdapter adapter;
private UserRepository repository;
public UserViewModel() {
repository = new UserRepository();
adapter = new UserAdapter();
// 配置Paging
PagedList.Config config = new PagedList.Config.Builder()
.setPageSize(20) // 每页显示的数量
.setEnablePlaceholders(false) // 不启用占位符
.build();
// 创建PagedList
userListLiveData = new MutableLiveData<>();
LivePagedListBuilder<Integer, User>(repository.getUsers(), config)
.build()
.observeForever(new Observer<PagedList<User>>() {
@Override
public void onChanged(PagedList<User> users) {
adapter.submitList(users); // 提交列表到适配器
}
});
}
// 获取适配器
public UserAdapter getAdapter() {
return adapter;
}
// 添加新用户
public void addUser() {
repository.addUser(new User("New User", 20));
}
}
8.4 ListAdapter与Paging库结合使用
ListAdapter可以与Paging库结合使用,高效处理大量数据。
// ListAdapter与Paging库结合使用
public class UserViewModel extends ViewModel {
private LiveData<PagedList<User>> userListLiveData;
private UserAdapter adapter;
public UserViewModel() {
adapter = new UserAdapter();
// 配置Paging
PagedList.Config config = new PagedList.Config.Builder()
.setPageSize(20) // 每页显示的数量
.setPrefetchDistance(10) // 预取距离
.setEnablePlaceholders(true) // 启用占位符
.build();
// 创建PagedList
userListLiveData = new LivePagedListBuilder<>(
new UserDataSourceFactory(), // 数据源工厂
config)
.build();
// 观察PagedList变化
userListLiveData.observeForever(new Observer<PagedList<User>>() {
@Override
public void onChanged(PagedList<User> users) {
adapter.submitList(users); // 提交列表到适配器
}
});
}
// 获取用户列表LiveData
public LiveData<PagedList<User>> getUserListLiveData() {
return userListLiveData;
}
// 获取适配器
public UserAdapter getAdapter() {
return adapter;
}
}
九、性能优化
9.1 减少不必要的更新
在数据模型中,应该只在数据真正发生变化时才通知UI更新。
// 优化数据模型,减少不必要的更新
public class User extends BaseObservable {
private String name;
private int age;
// 获取name属性
@Bindable
public String getName() {
return name;
}
// 设置name属性,仅在值不同时通知变化
public void setName(String name) {
if (!Objects.equals(this.name, name)) {
this.name = name;
notifyPropertyChanged(BR.name); // 通知name属性已变化
}
}
// 获取age属性
@Bindable
public int getAge() {
return age;
}
// 设置age属性,仅在值不同时通知变化
public void setAge(int age) {
if (this.age != age) {
this.age = age;
notifyPropertyChanged(BR.age); // 通知age属性已变化
}
}
}
9.2 使用合并更新
对于频繁变化的数据,可以合并更新以减少UI刷新次数。
// 使用合并更新的ViewModel
public class UserViewModel extends ViewModel {
private MutableLiveData<User> userLiveData = new MutableLiveData<>();
private Handler handler = new Handler(Looper.getMainLooper());
private Runnable updateRunnable;
private User pendingUser;
public UserViewModel() {
// 初始化用户数据
User user = new User("John Doe", 30);
userLiveData.setValue(user);
}
// 获取用户LiveData
public LiveData<User> getUser() {
return userLiveData;
}
// 批量更新用户信息
public void updateUserInfo(String name, int age, boolean isPremium) {
// 获取当前用户
User currentUser = userLiveData.getValue();
// 创建新用户对象
User newUser = new User(name, age);
newUser.setPremium(isPremium);
// 设置待更新的用户
pendingUser = newUser;
// 如果已经有一个更新任务在排队,移除它
if (updateRunnable != null) {
handler.removeCallbacks(updateRunnable);
}
// 创建新的更新任务,延迟执行以合并更新
updateRunnable = new Runnable() {
@Override
public void run() {
// 更新LiveData
userLiveData.setValue(pendingUser);
updateRunnable = null;
}
};
// 延迟100ms执行更新,合并短时间内的多次更新
handler.postDelayed(updateRunnable, 100);
}
}
9.3 使用生成的BR类
在通知属性变化时,使用生成的BR类常量可以提高性能。
// 使用BR类常量通知属性变化
public class User extends BaseObservable {
private String name;
private int age;
// 获取name属性
@Bindable
public String getName() {
return name;
}
// 设置name属性并通知变化
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name); // 使用BR类常量
}
// 获取age属性
@Bindable
public int getAge() {
return age;
}
// 设置age属性并通知变化
public void setAge(int age) {
this.age = age;
notifyPropertyChanged(BR.age); // 使用BR类常量
}
}
9.4 优化RecyclerView性能
通过优化RecyclerView的各种设置,可以提高列表的性能。
// 优化RecyclerView性能
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private UserViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 创建ViewModel
viewModel = new ViewModelProvider(this).get(UserViewModel.class);
// 创建DataBinding
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
// 设置ViewModel到DataBinding
binding.setViewModel(viewModel);
// 设置生命周期所有者
binding.setLifecycleOwner(this);
// 初始化RecyclerView
RecyclerView recyclerView = binding.recyclerView;
// 设置固定大小,提高性能
recyclerView.setHasFixedSize(true);
// 设置布局管理器
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
// 设置ItemAnimator
DefaultItemAnimator itemAnimator = new DefaultItemAnimator();
itemAnimator.setAddDuration(300); // 设置添加动画持续时间
itemAnimator.setRemoveDuration(300); // 设置移除动画持续时间
recyclerView.setItemAnimator(itemAnimator);
// 设置ItemDecoration
recyclerView.addItemDecoration(new DividerItemDecoration(this, layoutManager.getOrientation()));
// 设置适配器
recyclerView.setAdapter(viewModel.getAdapter());
}
}
9.5 使用RecyclerView的预取功能
RecyclerView的预取功能可以提前加载数据,提高滚动性能。
// 使用RecyclerView的预取功能
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private UserViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 创建ViewModel
viewModel = new ViewModelProvider(this).get(UserViewModel.class);
// 创建DataBinding
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
// 设置ViewModel到DataBinding
binding.setViewModel(viewModel);
// 设置生命周期所有者
binding.setLifecycleOwner(this);
// 初始化RecyclerView
RecyclerView recyclerView = binding.recyclerView;
// 设置布局管理器
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
// 设置预取距离
layoutManager.setItemPrefetchEnabled(true);
layoutManager.setInitialPrefetchItemCount(10); // 设置初始预取数量
recyclerView.setLayoutManager(layoutManager);
// 设置适配器
recyclerView.setAdapter(viewModel.getAdapter());
}
}
9.6 使用ViewHolder缓存
ViewHolder缓存可以减少View的创建和销毁,提高性能。
// 使用ViewHolder缓存
public class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserViewHolder> {
private List<User> users = new ArrayList<>();
// 设置用户列表
public void setUsers(List<User> users) {
this.users = users;
notifyDataSetChanged();
}
@NonNull
@Override
public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// 使用DataBindingUtil.inflate方法创建Item布局的绑定类
ItemUserBinding binding = ItemUserBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false);
return new UserViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
// 获取当前位置的用户
User user = users.get(position);
// 设置用户到绑定类
holder.binding.setUser(user);
// 执行挂起的绑定,确保数据立即更新到视图
holder.binding.executePendingBindings();
}
@Override
public int getItemCount() {
return users.size();
}
// ViewHolder类
public static class UserViewHolder extends RecyclerView.ViewHolder {
private ItemUserBinding binding;
public UserViewHolder(@NonNull ItemUserBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}
9.7 使用RecyclerView的缓存池
RecyclerView的缓存池可以重用ViewHolder,减少View的创建和销毁。
// 使用RecyclerView的缓存池
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private UserViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 创建ViewModel
viewModel = new ViewModelProvider(this).get(UserViewModel.class);
// 创建DataBinding
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
// 设置ViewModel到DataBinding
binding.setViewModel(viewModel);
// 设置生命周期所有者
binding.setLifecycleOwner(this);
// 初始化RecyclerView
RecyclerView recyclerView = binding.recyclerView;
// 设置布局管理器
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
// 设置缓存大小
recyclerView.setItemViewCacheSize(20); // 设置缓存大小为20个ViewHolder
// 设置RecycledViewPool
RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();
viewPool.setMaxRecycledViews(0, 20); // 设置类型0的最大缓存数量为20
recyclerView.setRecycledViewPool(viewPool);
// 设置适配器
recyclerView.setAdapter(viewModel.getAdapter());
}
}
9.8 使用ItemViewCacheSize
通过设置RecyclerView的ItemViewCacheSize,可以缓存更多的ViewHolder,减少布局计算。
// 设置ItemViewCacheSize
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private UserViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 创建ViewModel
viewModel = new ViewModelProvider(this).get(UserViewModel.class);
// 创建DataBinding
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
// 设置ViewModel到DataBinding
binding.setViewModel(viewModel);
// 设置生命周期所有者
binding.setLifecycleOwner(this);
// 初始化RecyclerView
RecyclerView recyclerView = binding.recyclerView;
// 设置布局管理器
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
// 设置ItemViewCacheSize
recyclerView.setItemViewCacheSize(20); // 缓存20个ViewHolder
// 设置适配器
recyclerView.setAdapter(viewModel.getAdapter());
}
}
9.9 使用setHasStableIds
通过设置setHasStableIds(true),可以提高RecyclerView的性能。
// 使用setHasStableIds
public class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserViewHolder> {
private List<User> users = new ArrayList<>();
public UserAdapter() {
setHasStableIds(true); // 设置稳定的ID
}
// 设置用户列表
public void setUsers(List<User> users) {
this.users = users;
notifyDataSetChanged();
}
@NonNull
@Override
public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// 使用DataBindingUtil.inflate方法创建Item布局的绑定类
ItemUserBinding binding = ItemUserBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false);
return new UserViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
// 获取当前位置的用户
User user = users.get(position);
// 设置用户到绑定类
holder.binding.setUser(user);
// 执行挂起的绑定,确保数据立即更新到视图
holder.binding.executePendingBindings();
}
@Override
public int getItemCount() {
return users.size();
}
@Override
public long getItemId(int position) {
// 返回稳定的ID
return users.get(position).getId();
}
// ViewHolder类
public static class UserViewHolder extends RecyclerView.ViewHolder {
private ItemUserBinding binding;
public UserViewHolder(@NonNull ItemUserBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}
9.10 使用AsyncDifferConfig
使用AsyncDifferConfig可以在后台线程计算差异,避免阻塞主线程。
// 使用AsyncDifferConfig
public class UserAdapter extends ListAdapter<User, UserAdapter.UserViewHolder> {
// 创建DiffUtil.ItemCallback实例
private static final DiffUtil.ItemCallback<User> DIFF_CALLBACK = new DiffUtil.ItemCallback<User>() {
@Override
public boolean areItemsTheSame(@NonNull User oldItem, @NonNull User newItem) {
// 判断是否是同一个用户(通过ID比较)
return oldItem.getId() == newItem.getId();
}
@Override
public boolean areContentsTheSame(@NonNull User oldItem, @NonNull User newItem) {
// 判断两个用户的内容是否相同
return oldItem.getName().equals(newItem.getName()) &&
oldItem.getAge() == newItem.getAge();
}
};
// 构造方法
public UserAdapter() {
super(new AsyncListDifferConfig.Builder<User>(DIFF_CALLBACK)
.setBackgroundThreadExecutor(Executors.newSingleThreadExecutor()) // 设置后台线程执行器
.build());
}
@NonNull
@Override
public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// 使用DataBindingUtil.inflate方法创建Item布局的绑定类
ItemUserBinding binding = ItemUserBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false);
return new UserViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
// 获取当前位置的用户
User user = getItem(position);
// 设置用户到绑定类
holder.binding.setUser(user);
// 执行挂起的绑定,确保数据立即更新到视图
holder.binding.executePendingBindings();
}
// ViewHolder类
public static class UserViewHolder extends RecyclerView.ViewHolder {
private ItemUserBinding binding;
public UserViewHolder(@NonNull ItemUserBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}
十、双向数据绑定
10.1 双向数据绑定概述
双向数据绑定允许数据的变化自动更新到UI,同时UI的变化也能自动更新到数据。
<!-- 双向数据绑定示例 -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.example.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={user.name}" /> <!-- 使用@={}语法实现双向绑定 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.name}" /> <!-- 单向绑定 -->
</LinearLayout>
</layout>
10.2 实现双向数据绑定
下面是一个实现双向数据绑定的完整示例。
// 支持双向数据绑定的User类
public class User extends BaseObservable {
private ObservableField<String> name = new ObservableField<>();
private ObservableInt age = new ObservableInt();
// 获取name属性
public ObservableField<String> getName() {
return name;
}
// 设置name属性
public void setName(String name) {
this.name.set(name);
}
// 获取age属性
public ObservableInt getAge() {
return age;
}
// 设置age属性
public void setAge(int age) {
this.age.set(age);
}
}
<!-- 双向数据绑定布局 -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.example.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter name"
android:text="@={user.name}" /> <!-- 双向绑定到name属性 -->
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter age"
android:inputType="number"
android:text="@={String.valueOf(user.age)}" /> <!-- 双向绑定到age属性 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Name: @{user.name}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Age: @{String.valueOf(user.age)}" />
</LinearLayout>
</layout>
// 在Activity中使用双向数据绑定
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 创建DataBinding
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
// 创建User对象
user = new User();
user.setName("John Doe");
user.setAge(30);
// 设置User到DataBinding
binding.setUser(user);
// 设置生命周期所有者
binding.setLifecycleOwner(this);
}
}
10.3 自定义双向数据绑定属性
除了内置的双向绑定属性,还可以自定义双向绑定属性。
// 自定义双向数据绑定适配器
public class BindingAdapters {
// 自定义双向绑定属性
@InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
public static String getText(EditText view) {
return view.getText().toString();
}
// 设置文本变化监听器
@BindingAdapter(value = {"android:text", "android:textAttrChanged"}, requireAll = false)
public static void setText(EditText view, String text, final InverseBindingListener listener) {
// 如果文本不同,则设置新文本
if (!view.getText().toString().equals(text)) {
view.setText(text);
}
// 设置文本变化监听器
view.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// 不需要实现
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// 不需要实现
}
@Override
public void afterTextChanged(Editable s) {
// 通知文本已变化
listener.onChange();
}
});
}
}
10.4 在RecyclerView中使用双向数据绑定
在RecyclerView中也可以使用双向数据绑定。
<!-- 列表项布局 -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.example.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter name"
android:text="@={user.name}" /> <!-- 双向绑定到name属性 -->
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter age"
android:inputType="number"
android:text="@={String.valueOf(user.age)}" /> <!-- 双向绑定到age属性 -->
</LinearLayout>
</layout>
// 在RecyclerView适配器中使用双向数据绑定
public class UserAdapter extends ListAdapter<User, UserAdapter.UserViewHolder> {
// 创建DiffUtil.ItemCallback实例
private static final DiffUtil.ItemCallback<User> DIFF_CALLBACK = new DiffUtil.ItemCallback<User>() {
@Override
public boolean areItemsTheSame(@NonNull User oldItem, @NonNull User newItem) {
// 判断是否是同一个用户(通过ID比较)
return oldItem.getId() == newItem.getId();
}
@Override
public boolean areContentsTheSame(@NonNull User oldItem, @NonNull User newItem) {
// 判断两个用户的内容是否相同
return oldItem.getName().get().equals(newItem.getName().get()) &&
oldItem.getAge().get() == newItem.getAge().get();
}
};
// 构造方法
public UserAdapter() {
super(DIFF_CALLBACK);
}
@NonNull
@Override
public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// 使用DataBindingUtil.inflate方法创建Item布局的绑定类
ItemUserBinding binding = ItemUserBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false);
return new UserViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
// 获取当前位置的用户
User user = getItem(position);
// 设置用户到绑定类
holder.binding.setUser(user);
// 执行挂起的绑定,确保数据立即更新到视图
holder.binding.executePendingBindings();
}
// ViewHolder类
public static class UserViewHolder extends RecyclerView.ViewHolder {
private ItemUserBinding binding;
public UserViewHolder(@NonNull ItemUserBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}