引言
有时候,感觉老外的文章,思考深度比国内深很多,最近看到一篇如何
在RecyclerView中使用MVP的文章,感觉挺好,特翻译过来进行记录。
原文
RecyclerView in MVP — Passive view’s approach
译文
每次当使用RecyclerView(或者其他需要适配器的视图)的时候,最重要的就是如何处理数据。
很多时候,大家倾向于在Adapter中持有一个Collection合集(比如说List),用它来保存需要显示的数据。在MVP(Model-View-Presenter)模式中,我们一般会将数据保存在Presenter中;而在Adapter中持有数据,是一种很糟糕的情况,这会导致列表在两个不同的地方被引用,但我们数据发生变化时,我们需要同时变更两处的数据。
首先,在adapter中存储数据并能够操纵它(通过移除,增加或者更新相关元素)打破了被动视图的原理和一般的MVP模式规则,因为所有的演示的逻辑都应该是presenter的任务。那么如何解决这个问题呢?
首先,我们先通过我们的adapter能够通知presenter它所需要的数据,将每一个条目作为一种MVP视图进行展示。
public class RepositoriesRecyclerAdapter extends RecyclerView.Adapter<ReposRecyclerAdapter.RepoViewHolder> {
private final RepositoriesListPresenter presenter;
public ReposRecyclerAdapter(RepositoriesListPresenter repositoriesPresenter) {
this.presenter = repositoriesPresenter;
}
@Override
public RepositoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new RepositoryViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.cell_repo_view, parent, false));
}
@Override
public void onBindViewHolder(RepositoryViewHolder holder, int position) {
presenter.onBindRepositoryRowViewAtPosition(position, holder);
}
@Override
public int getItemCount() {
return presenter.getRepositoriesRowsCount();
}
}
你可能注意到了,我们将Holder传给了我们的presenter。
你个混蛋,persenter不能依赖于视图层!你现在将persenter和holder紧密耦合在一起了。
别担心,我们先来看看RepositoryViewHolder 的实现方式。
public class RepositoryViewHolder extends RecyclerView.ViewHolder implements RepositoryRowView {
TextView titleTextView;
TextView starsCountTextView;
public RepositoryViewHolder(View itemView) {
super(itemView);
titleTextView = itemView.findViewById(R.id.repoTitleText);
starsCountTextView = itemView.findViewById(R.id.repoStarsCountText);
}
@Override
public void setTitle(String title) {
titleTextView.setText(title);
}
@Override
public void setStarCount(int starCount) {
starsCountTextView.setText(String.format("%s ★", starCount));
}
}
正如你所视,我们的RepositoryViewHolder是RepositoryRowView的具体实现,这是一种典型的MVP中View层的接口。
interface RepositoryRowView {
void setTitle(String title);
void setStarCount(int starCount);
}
presenter中调用的是接口,而不是具体的实现。
public class RepositoriesListPresenter {
private final List<Repository> repositories;
...
public void onBindRepositoryRowViewAtPosition(int position, RepositoryRowView rowView) {
Repository repo = repositories.get(position);
rowView.setStarCount(repo.getStarsCount());
rowView.setTitle(repo.getTitle());
}
public int getRepositoriesRowsCount() {
return repositories.size();
}
...
}
这样一来,每行需要的数据以及所有的逻辑都转移到了persent而,而且不会破坏简洁的架构(persenter对于视图层的具体实现一无所知,也不知道android框架的任何内容)。
总结
文章很简洁,将我们平时使用的adapter进行了更仔细的分层,将业务逻辑和展示进行了隔离,这是一种很好的实现理念,感觉很值得学习。