github上有一个关于MVP模式学习的实例https://github.com/antoniolg/androidmvp,虽然只有简单的几个类,却收获了几千个星。这个例子确实通俗易懂,直观的体现出了MVP模式的特点:
考虑这样一个需求,页面显示一个列表,数据源需要特别获取。
分析一下这个过程,页面首先显示出进度提示框表示正在加载,然后数据源获取数据并传给页面的列表进行渲染。
MVP模式,model ,view,presenter。。视图层只处理UI操作,包括显示dialog,拿到数据后的处理(关闭dialog并渲染列表),这里可以细分为关闭dialog和为列表的适配器设置数据列表。 presenter是视图层和数据层沟通的桥梁,控制着双方的行为。 数据源则负责获取数据并返回。
首先对三个类进行抽象:
1.视图层:
package com.antonioleiva.mvpexample.app.main;
import java.util.List;
public interface MainView {
void showProgress();
void hideProgress();
void setItems(List<String> items);
void showMessage(String message);
}
package com.antonioleiva.mvpexample.app.main;
import java.util.List;
public interface FindItemsInteractor {
interface OnFinishedListener {
void onFinished(List<String> items);
}
void findItems(OnFinishedListener listener);
}
这里一个接口用来返回拿到的数据,接口中的方法将数据传入。在获取数据的方法里将这个接口传入,接口的实例化是在代理中实现的。
3.代理层:
package com.antonioleiva.mvpexample.app.main;
public interface MainPresenter {
void onResume();
void onItemClicked(int position);
void onDestroy();
}
视图层在调用onResume和onDestroy时的逻辑也要通过代理的方法实现。
下面是创建的实例,首先是数据实例:
1.数据层:
public class FindItemsInteractorImpl implements FindItemsInteractor {
@Override public void findItems(final OnFinishedListener listener) {
new Handler().postDelayed(new Runnable() {
@Override public void run() {
listener.onFinished(createArrayList());
}
}, 2000);
}
private List<String> createArrayList() {
return Arrays.asList(
"Item 1",
"Item 2",
"Item 3",
"Item 4",
"Item 5",
"Item 6",
"Item 7",
"Item 8",
"Item 9",
"Item 10"
);
}
}
只有两个方法,也可以看成只有一个方法,就是拿到数据并通过回调接口返回。
2.视图层:
public class MainActivity extends Activity implements MainView, AdapterView.OnItemClickListener {
private ListView listView;
private ProgressBar progressBar;
private MainPresenter presenter;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView) findViewById(R.id.list);
listView.setOnItemClickListener(this);
progressBar = (ProgressBar) findViewById(R.id.progress);
presenter = new MainPresenterImpl(this, new FindItemsInteractorImpl());
}
@Override protected void onResume() {
super.onResume();
presenter.onResume();
}
@Override public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_settings:
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override protected void onDestroy() {
presenter.onDestroy();
super.onDestroy();
}
@Override public void showProgress() {
progressBar.setVisibility(View.VISIBLE);
listView.setVisibility(View.INVISIBLE);
}
@Override public void hideProgress() {
progressBar.setVisibility(View.INVISIBLE);
listView.setVisibility(View.VISIBLE);
}
@Override public void setItems(List<String> items) {
listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items));
}
@Override public void showMessage(String message) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
@Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
presenter.onItemClicked(position);
}
}
视图层拥有一个代理实例,将几个生命阶段的逻辑以及列表项点击全部托管给代理进行处理,自己只负责实现接口中的几个ui操作方法。
3.代理层:
public class MainPresenterImpl implements MainPresenter, FindItemsInteractor.OnFinishedListener {
private MainView mainView;
private FindItemsInteractor findItemsInteractor;
public MainPresenterImpl(MainView mainView, FindItemsInteractor findItemsInteractor) {
this.mainView = mainView;
this.findItemsInteractor = findItemsInteractor;
}
@Override public void onResume() {
if (mainView != null) {
mainView.showProgress();
}
findItemsInteractor.findItems(this);
}
@Override public void onItemClicked(int position) {
if (mainView != null) {
mainView.showMessage(String.format("Position %d clicked", position + 1));
}
}
@Override public void onDestroy() {
mainView = null;
}
@Override public void onFinished(List<String> items) {
if (mainView != null) {
mainView.setItems(items);
mainView.hideProgress();
}
}
public MainView getMainView() {
return mainView;
}
}
首先代理的构造器要传入视图和数据两个实例,然后通过调用这两个实例的方法来完成控制。这里代理的几个任务有,在视图销毁时设置解绑,对列表项点击事件的响应,在视图刚开始时控制视图显示等待中并开始控制数据源获取数据,在数据获取完成后通过实现的回调接口将数据传给视图层。
需要注意的是代理实现了数据层的接口,以此来接收并处理数据。