申明:低级码农问题解决中的参考和解决后的小结,仅用于个人记录,能力有限,可能有些错误,缺陷不自知,欢迎在评论中指正,谢谢!
简单了解mvc
mvp是对android中mvc的不足进行的优化。为了更容易理解,先简单介绍一下android中的mvc模式。
Model:狭义讲就是bean数据,数据的获取和存储由充当Control层的Activity完成,这导致Activity太庞大了。所以现在把数据的获取和存储,封装网络框架和数据库组件中,把他们也算作Model。
View:布局文件。但是布局文件的功能太弱了,Activity也必须承担部分View层的功能。
Control:毫无悬念由Activity充当。接受用户操作,请求数据。获取到数据,更新到UI上。
下图是mvc模式的结构。
下面是一个简单的例子,点击按钮获取数据,然后更新到UI上。这个例子中,Activity的工作很繁杂,响应用户操作,然后获取数据,在接收数据并更新到UI上。
public class SecondActivity extends Activity { Handler handler; TextView tv; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.secondactivity); setTitle("second"); handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 0) { String str = (String) msg.obj; tv.setText(str); } } }; tv = (TextView) findViewById(R.id.tv); findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread() { @Override public void run() { StringBuilder str = new StringBuilder(); for (int i = 0; i < 5; i++) { str.append("学号 ").append(i).append("\n"); } Message msg = handler.obtainMessage(0); msg.obj = str.toString(); msg.sendToTarget(); } }.start(); } }); } }
简介mvp
mvp的优点是简化了Activity,Activity只负责展示数据和用户交互。真正意义上隔离了View和Model。缺点是很依赖场景的抽象,然后就是没增加一个场景,要增加很多接口方法。结构如下:
改造为mvp的过程
包括对view,model,presenter三部分。
View应该根据实际需要的UI变化的场景,合理提供更新UI的接口方法。注意是更新场景提供接口,而不是根据控件。比如开始获取数据时,UI要做的改变如显示progressbar、按钮不可点击、旧的数据的清空;获取到数据后,隐藏progressbar,显示数据等。要根据这两个场景提供方法,而不是分别提供progressbar,按钮,显示数据的ListView的方法,让Presenter决定哪些要变,怎么变。
本文定义了一个IView接口,Activity作为View层,实现此接口。
public interface IView { void refreshUI(String s); void showProgressbar(); /* 根据场景,细化更新UI的方法 void refreshPartUI(); void showProgressbar(); void hideProgressbar(); */ }
/** Activity实现IView */ public class SecondActivity extends Activity implements IView { TextView tv; Presenter presenter; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.secondactivity); setTitle("second"); presenter = new Presenter(SecondActivity.this); tv = (TextView) findViewById(R.id.textview); findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 调用Presenter获取数据 presenter.getData(); } }); } /* * 实现IView的接口方法 */ @Override public void refreshUI(final String s) { runOnUiThread(new Runnable() { @Override public void run() { tv.setText(s); } }); } @Override public void showProgressbar() { // 这里弹出Progressbar } }
Model也是根据View中定义的场景,提供对应获取数据的方法,获取到数据后,通知Presenter数据已经拿到。本文通知Presenter的方式是提供一个回调接口,Presenter实现此回调。
public class Model { CallBack callBack; public Model(CallBack callBack) { this.callBack = callBack; } public void requestData() { ArrayList<String> list = new ArrayList<>(); for (int i = 0; i < 5; i++) { list.add("学号 " + i + "\n"); } if (callBack != null) { String str = new Gson().toJson(list); callBack.onGetData(str); } } /** 通过回到接口通知Presenter数据获取完成 */ public interface CallBack { void onGetData(String str); } }
Presenter,要做的是调用View的全部方法,实现Model的内部回调接口。View中定义的更新UI的方法,全部应该在Presenter中被调用,通过Presenter内部持有的View对象。没有被调用的方法,就是多余的方法。调用时需要进行比较复杂的判断来确定调用什么方法,就是把UI更新逻辑放到Presenter中了。这两种情况都是场景没有抽象好的情况。
/** * 连接View层和Model层,有两部分核心功能 * 1、对View层提供获取数据的方法,该方法内部调用Model完成 * 2、接受Model的数据变化,通知View更新UI */ public class Presenter implements Model.CallBack { Model model; IView view; public Presenter(IView view) { this.view = view; model = new Model(this); } /** 对View暴露,内部调用Model */ public void getData() { view.showProgressbar(); model.requestData(); } /** 接收Model的变化,通过内部持有的View对象更新UI(是View更新的UI) */ @Override public void onGetData(String str) { view.refreshUI(str); } }