前言
以前在学习Android的时候,第一次接触设计模式,就是MVC。当时,写的代码不多,听得懵懵懂懂。在后来,深切感受到是在第一次组队写项目的时候,由于时间紧迫,前期我们就做了分工,我当时的任务就是搞定接口文档,并封装一个网络请求类,别的组员就暂时通过假数据做着自己的模块。
突然,我就感觉到其实 APP 很简单,就是UI+数据,没有真正数据的 APP 是死的,一旦有了数据与之互动,那么它就变活了,当然了,活得这么样又是另外一码事。那么,一手UI,一手数据,UI如何显示,数据什么时候更新,什么时候更新UI等等中间的互动就需要一个桥梁。
MVC
MVC, Model View Controller,这里的桥梁就是Controller,简单来说就是通过 Controller 的控制区操作 Model 层的数据,然后返回给 View 层显示。
- Model : javaBean 业务逻辑相关
- View : xml文件
- Controller : activity fragment
在这里看 View 层,它是 xml 文件,它仅仅是一些布局文件,控制能力基本没有,如果想要去控制其中的控件隐藏/显示,只能将代码写到 activity 中,造成了 activity 既是 View 层也是 Controller 层的窘境。也就造成了 android中大部分app用的是view-model 。
Most of the modern Android applications just use View-Model architecture,everything is connected with Activity.
这就导致了,如果是一个逻辑很复杂的页面,activity/fragment的代码将相当的庞大臃肿,维护起来也不方便。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
initRetrofit();
initView();
initEvent();
initData();
}
private void initData() {
getDatas(REFRESH_DATA);
}
private void initRetrofit() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://gank.io/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
mGankioService = retrofit.create(GankioService.class);
}
private void initEvent() {
mSr.setOnRefreshListener(mOnRefreshListener);
}
private void initView() {
mSr = (SwipeRecyclerView) findViewById(R.id.activity_swipe_recycler);
/**设置布局管理器*/
mLayoutManager = new GridLayoutManager(this, 3);
mSr.setLayoutManager(mLayoutManager);
/**设置adapter*/
mMyadapter = new MyAdapter();
mSr.setAdapter(mMyadapter);
}
class MyAdapter extends SwipeRecyclerView.Adapter<MyAdapter.MyViewHolder> {
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder vh = new MyViewHolder(LayoutInflater.from(HomeActivity.this).inflate(R.layout.activity_recycler_item, parent, false));
return vh;
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
final String url = mDatas.get(position).url;
Glide.with(holder.mImageView.getContext())
.load(url)
.centerCrop()
.placeholder(R.color.app_primary_dark)
.crossFade()
.into(holder.mImageView);
holder.mImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(HomeActivity.this, BitmapActivity.class);
intent.putExtra("url", url);
startActivity(intent);
}
});
}
@Override
public int getItemCount() {
if (mDatas == null)
return 0;
return mDatas.size();
}
@Override
protected void loadMoreDatas(RecyclerView recyclerView, int dx, int dy) {
int position = mLayoutManager.findLastVisibleItemPosition();
int itemCount = mMyadapter.getItemCount();
if (position + 1 == itemCount) {
getDatas(LOADMORE_DATA);
}
}
public class MyViewHolder extends RecyclerView.ViewHolder {
ImageView mImageView;
public MyViewHolder(View itemView) {
super(itemView);
mImageView = (ImageView) itemView.findViewById(R.id.item_imageview);
}
}
}
private void getDatas(final int action) {
if (action == REFRESH_DATA) {
page = 1;
} else {
page++;
}
mGankioService.getWelfare(50, page)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<BaseModel<List<Welfare>>>() {
@Override
public void onStart() {
mSr.mSwipeRefreshLayout.setRefreshing(true);
}
@Override
public void onCompleted() {
mSr.mSwipeRefreshLayout.setRefreshing(false);
}
@Override
public void onError(Throwable e) {
Log.i(">>>", "onError " + e.toString());
}
@Override
public void onNext(BaseModel<List<Welfare>> listBaseModel) {
if (action == REFRESH_DATA) {
mDatas.clear();
}
mDatas.addAll(listBaseModel.results);
mMyadapter.notifyDataSetChanged();
}
});
}
private SwipeRecyclerView.OnRefreshListener mOnRefreshListener = new SwipeRecyclerView.OnRefreshListener() {
@Override
public void onRefresh() {
getDatas(REFRESH_DATA);
}
};
这是我之前用 mvc 写的gank.io其中一个显示妹子图的 activity。可以看到通过 retrofit 获取数据,并显示数据。
MVP
上文已经提到了 MVC 的缺陷,接着便演化出来了 MVP 。
- Model : javaBean 业务逻辑相关
- View : activity fragemnt
- presenter : 负责完成View于Model间的交互
从图中可以看出, View 层和 Model 层已经完全的解耦了,以前的 Controller 换成了 Presenter 作为它们之间的桥梁,用于操作 View 层发出的事件传递到 Presenter 中,Presenter 层再去操作 Model 层,获取数据并返回给 View 层。
以下就将上文的代码利用 MVP 实现。
-
Model
在以上代码中,对数据进行的操作有,最开始的初始化数据,刷新数据,加载更多数据。对此创建一个
HomeModel
public interface HomeModel { void initializeDatas(); void refreshDatas(); void loadMoreDatas(); }
实现之:
public class HomeModelmp implements HomeModel { public static final int REFRESH_DATA = 1; public static final int LOADMORE_DATA = 2; private HomeOnListener mListener; private int page; public HomeModelmp(HomeOnListener l) { this.mListener = l; } @Override public void initializeDatas() { getDatas(REFRESH_DATA); } @Override public void refreshDatas() { getDatas(REFRESH_DATA); } @Override public void loadMoreDatas() { getDatas(LOADMORE_DATA); } private void getDatas(final int action) { if (action == REFRESH_DATA) { page = 1; } else { page++; } ServiceManager.getInstance().mService.getWelfare(20, page) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<BaseModel<List<Welfare>>>() { @Override public void onStart() { mListener.onStart(); } @Override public void onCompleted() { mListener.onCompleted(); } @Override public void onError(Throwable e) { mListener.onError(e); } @Override public void onNext(BaseModel<List<Welfare>> listBaseModel) { mListener.onNext(action, listBaseModel); } }); } public interface HomeOnListener { void onStart(); void onCompleted(); void onError(Throwable e); void onNext(int action, BaseModel<List<Welfare>> listBaseModel); } }
-
View
View 层就是 activity ,其中将只有纯粹的UI操作。同样创建一个接口:
public interface HomeView { /** * 加载中 */ void onLoading(); /** * 加载完成 */ void onLoadCompleted(); /** * 加载布局 */ View inflateLayout(ViewGroup parent, boolean b); /** * 跳转 */ void skipActivity(String url); /** * 获取最后一个可见item位置 */ int findLastPosition(); }
将activity实现该接口:
public class HomeActivity extends AppCompatActivity implements HomeView { private SwipeRecyclerView mSr; private HomePresenter mHomePresenter; private GridLayoutManager mLayoutManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); if (mHomePresenter == null) { mHomePresenter = new HomePresenter(this); } initView(); initEvent(); initData(); } private void initData() { mHomePresenter.initialize(); } private void initEvent() { mSr.setOnRefreshListener(mOnRefreshListener); } private void initView() { mSr = (SwipeRecyclerView) findViewById(R.id.activity_swipe_recycler); /**设置布局管理器*/ mLayoutManager = new GridLayoutManager(this, 3); mSr.setLayoutManager(mLayoutManager); /**设置adapter*/ mHomePresenter.setAdapter(mSr); } @Override public void onLoading() { mSr.mSwipeRefreshLayout.setRefreshing(true); } @Override public void onLoadCompleted() { mSr.mSwipeRefreshLayout.setRefreshing(false); } @Override public View inflateLayout(ViewGroup parent, boolean b) { return LayoutInflater.from(HomeActivity.this).inflate(R.layout.activity_recycler_item, parent, false); } @Override public void skipActivity(String url) { Intent intent = new Intent(HomeActivity.this, BitmapActivity.class); intent.putExtra("url", url); startActivity(intent); } @Override public int findLastPosition() { return mLayoutManager.findLastVisibleItemPosition(); } private SwipeRecyclerView.OnRefreshListener mOnRefreshListener = new SwipeRecyclerView.OnRefreshListener() { @Override public void onRefresh() { mHomePresenter.onRefreshData(); } };
-
Presenter
Presenter作为 View 和 Model 之间交互的桥梁,也就是在合适的地方调用各自的方法。
public class HomePresenter implements HomeModelmp.HomeOnListener { private HomeView mHomeView; private List<Welfare> mDatas = new ArrayList<>(); private HomeActivity view; private HomeModel mHomeModel; private HomeAdapter mAdapter; public HomePresenter(HomeView homeView) { this.mHomeView = homeView; this.mHomeModel = new HomeModelmp(this); } public void initialize(){ mDatas.clear(); mHomeModel.initializeDatas(); } public void onRefreshData() { mDatas.clear(); mHomeModel.refreshDatas(); } @Override public void onStart() { mHomeView.onLoading(); } @Override public void onCompleted() { mHomeView.onLoadCompleted(); } @Override public void onError(Throwable e) { } @Override public void onNext(int action, BaseModel<List<Welfare>> listBaseModel) { if (action == HomeModelmp.REFRESH_DATA) { mDatas.clear(); } mDatas.addAll(listBaseModel.results); mAdapter.notifyDataSetChanged(); } public void setAdapter(SwipeRecyclerView sr) { mAdapter = new HomeAdapter(); sr.setAdapter(mAdapter); } public class HomeAdapter extends SwipeRecyclerView.Adapter<HomeAdapter.MyViewHolder> { @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { MyViewHolder vh = new MyViewHolder(mHomeView.inflateLayout(parent, false)); return vh; } @Override public void onBindViewHolder(MyViewHolder holder, int position) { final String url = mDatas.get(position).url; Glide.with(holder.mImageView.getContext()) .load(url) .centerCrop() .placeholder(R.color.app_primary_dark) .crossFade() .into(holder.mImageView); holder.mImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mHomeView.skipActivity(url); } }); } @Override public int getItemCount() { if (mDatas == null) return 0; return mDatas.size(); } @Override protected void loadMoreDatas(RecyclerView recyclerView, int dx, int dy) { int position = mHomeView.findLastPosition(); int itemCount = getItemCount(); if (position + 1 == itemCount) { mHomeModel.loadMoreDatas(); } } public class MyViewHolder extends RecyclerView.ViewHolder { public ImageView mImageView; public MyViewHolder(View itemView) { super(itemView); mImageView = (ImageView) itemView.findViewById(R.id.item_imageview); } } } }
Ok,大功告成。这里仅是贴出了关键代码,github地址:Gnak.io:https://github.com/cgzysan/Gank.io</br>
参考资料: