MVVM+DataBinding环境下Activity中Adapter设置及ViewModel数据填充问题
嘿,刚接触MVVM和DataBinding的话,你的问题绝对是入门阶段最常见的困惑之一,我来一步步帮你捋清楚,保证你能快速上手~
一、先解决Activity中设置Adapter的问题
假设你已经用DataBinding完成了ViewHolder和Adapter的编写,在Activity里设置Adapter的核心逻辑其实和传统方式类似,只是借助DataBinding来简化控件的获取。我先补全你的ScoresActivity代码,带你走一遍流程:
public class ScoresActivity extends AppCompatActivity { private ActivityScoresBinding binding; // 自动生成的DataBinding类,对应你的activity_scores.xml布局 private ScoresAdapter scoresAdapter; // 你已经实现的Adapter类 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initBinding(); initAdapter(); // 后面会加入ViewModel的初始化逻辑 } private void initBinding() { // 用DataBinding替代传统的setContentView,直接获取布局绑定实例 binding = DataBindingUtil.setContentView(this, R.layout.activity_scores); // 让DataBinding感知Activity生命周期,配合LiveData使用更安全 binding.setLifecycleOwner(this); } private void initAdapter() { // 1. 创建Adapter实例(如果你的Adapter需要初始数据,也可以在这里传入空列表) scoresAdapter = new ScoresAdapter(); // 2. 绑定Adapter到RecyclerView(通过binding直接访问布局中的RecyclerView,无需findViewById) binding.recyclerView.setAdapter(scoresAdapter); // 别忘了设置LayoutManager,否则RecyclerView不会显示内容 binding.recyclerView.setLayoutManager(new LinearLayoutManager(this)); } }
二、要不要用ViewModel填充ArrayList?当然要!
MVVM的核心就是UI与数据逻辑分离,ViewModel就是专门用来托管数据和业务逻辑的角色,用它来管理你的ArrayList有两大好处:
- 解耦:Activity只负责UI展示,数据加载/填充交给ViewModel,代码更清晰
- 数据持久化:屏幕旋转等配置变化时,ViewModel会保留数据,不会丢失
下面是具体的实现步骤:
1. 编写ViewModel类,托管可观察数据
我们用LiveData来包装数据列表,因为它能自动感知Activity生命周期,避免内存泄漏:
public class ScoresViewModel extends ViewModel { // 用MutableLiveData存储可修改的列表数据,对外暴露不可变的LiveData防止外部篡改 private final MutableLiveData<List<Score>> scoresLiveData = new MutableLiveData<>(); public ScoresViewModel() { // 初始化数据,这里用模拟数据举例,实际可以替换成网络请求/本地数据库查询 loadMockScores(); } // 对外提供观察数据的入口 public LiveData<List<Score>> getScoresLiveData() { return scoresLiveData; } // 模拟填充ArrayList的方法 private void loadMockScores() { List<Score> scoreList = new ArrayList<>(); scoreList.add(new Score("张三", 95)); scoreList.add(new Score("李四", 88)); scoreList.add(new Score("王五", 92)); // 更新LiveData,通知观察者(也就是Activity)数据变化 scoresLiveData.setValue(scoreList); } // 如果是异步加载数据(比如网络请求),要用postValue在子线程更新数据 public void loadRemoteScores() { // 假设这里发起网络请求,请求成功后: // scoresLiveData.postValue(remoteScoreList); } }
对应的Score数据实体类(记得写getter方法,DataBinding需要通过getter访问属性):
public class Score { private final String name; private final int score; public Score(String name, int score) { this.name = name; this.score = score; } public String getName() { return name; } public int getScore() { return score; } }
2. 在Activity中关联ViewModel,观察数据变化
修改ScoresActivity,加入ViewModel的初始化和数据观察逻辑:
public class ScoresActivity extends AppCompatActivity { private ActivityScoresBinding binding; private ScoresAdapter scoresAdapter; private ScoresViewModel scoresViewModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initBinding(); initViewModel(); initAdapter(); } private void initBinding() { binding = DataBindingUtil.setContentView(this, R.layout.activity_scores); binding.setLifecycleOwner(this); } private void initViewModel() { // 通过ViewModelProvider获取ViewModel实例,确保生命周期与Activity绑定 scoresViewModel = new ViewModelProvider(this).get(ScoresViewModel.class); // 观察LiveData的变化,数据更新时通知Adapter scoresViewModel.getScoresLiveData().observe(this, scores -> { // 调用Adapter的方法更新数据 scoresAdapter.setScores(scores); }); } private void initAdapter() { scoresAdapter = new ScoresAdapter(); binding.recyclerView.setAdapter(scoresAdapter); binding.recyclerView.setLayoutManager(new LinearLayoutManager(this)); } }
3. 让Adapter支持数据更新
你的Adapter需要提供一个更新数据的方法,替换内部的列表并通知RecyclerView刷新:
public class ScoresAdapter extends RecyclerView.Adapter<ScoresAdapter.ScoreViewHolder> { private List<Score> scoreList = new ArrayList<>(); @NonNull @Override public ScoreViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { // 用DataBinding加载Item布局 ItemScoreBinding binding = ItemScoreBinding.inflate( LayoutInflater.from(parent.getContext()), parent, false ); return new ScoreViewHolder(binding); } @Override public void onBindViewHolder(@NonNull ScoreViewHolder holder, int position) { Score score = scoreList.get(position); // 把数据绑定到Item的Binding对象 holder.binding.setScore(score); // 立即执行绑定,避免UI更新延迟 holder.binding.executePendingBindings(); } @Override public int getItemCount() { return scoreList.size(); } // 对外提供数据更新方法 public void setScores(List<Score> scores) { this.scoreList.clear(); this.scoreList.addAll(scores); // 入门阶段用notifyDataSetChanged即可,进阶可以用DiffUtil提升性能 notifyDataSetChanged(); } public static class ScoreViewHolder extends RecyclerView.ViewHolder { private final ItemScoreBinding binding; public ScoreViewHolder(ItemScoreBinding binding) { super(binding.getRoot()); this.binding = binding; } } }
三、额外小提示
- 如果想让布局直接和ViewModel交互,可以在
activity_scores.xml中声明ViewModel变量,不过对于RecyclerView列表,还是通过Adapter处理更灵活。 - 进阶阶段可以用
DiffUtil替代notifyDataSetChanged(),只刷新变化的Item,提升RecyclerView性能。
内容的提问来源于stack exchange,提问作者Riddhi Shah




