在MVP模式中,我们知道通常Presenter层会与View层进行绑定联系。由Presenter层控制View,同时,View层也将调用Presenter进行逻辑及界面显示。View层通常是我们的Fragment或者Activity,那么本文就注册的界面(RegisterFragment) 对最顶部的Presenter,View一直到BasePresenter,BaseView进行封装。
首先,我们创建一个RegisterFragment和一个RegisterPresenter,它们必须按照一定得规矩进行。(此处RegisterFragment即为View层)
So我们创建一个接口文件RegisterContract,其中包含两个内部接口View以及Presenter
那么接口文件中应该有什么方法呢?
在Presenter中,我们应该有注册的方法Register。也应该有presenter的开启和销毁方法(start,destroy)。
在View中,我们应该有注册成功后的回掉方法用以告诉Fragment我们注册成功可以开始下一步操作。
那么你会发现在这些方法中并非所有方法都是RegisterFragment或者RegisterPresenter共有的,so我们进行提取,我们将不属于Register注册功能特有的方法提取到BaseContract中。于是,我们的RegisterContract可以写了
//RegisterContract的特有契约 interface RegisterContract { interface View{ //注册成功 void registerSuccess(); } interface Presenter{ //发起一个注册 void register(String phone,String name,String password); }
}(此处RegisterContract未完成,还未继承自基类Contract) 那么接下来该写BaseContract了,在此之前我们想一个问题,每个View层中都需要一个Presenter对吧,那么我们就需要在View层中也就是RegisterFragment中设置Presenter,也就是将View层与Presenter层进行绑定,所以我们需要在基本契约(BaseContract)中为View添加一个方法setPresenter(Presenter)
我们所有的Presenter都必须继承BaseContract.Presenter的实现类BasePresenter(一会儿新建),但它是什么种类,是RegisterPresenter还是LoginRegister我们不知道,所以在setPresenter是里面的参数必须是一个泛型,那么这个泛型我们就需要在创建View接口时为它定义好,也就是T extend Presenter,所以我们可以写BaseContract了。
/**
-
MVP格式中公共的基本契约 */ public interface BaseContract { interface View<T extends Presenter> {//此处的presenter即为下面定义的Presenter //公共的:显示一个字符串错误 void showError(@StringRes int str); //公共的:显示进度条 void showLoading(); //支持设置一个Presenter void setPresenter(T presenter); } interface Presenter { //共同的开始方法 void start(); //共同的销毁触发 void destroy(); } } 公共契约BaseContract有了,我们就可以更改我们的特有契约RegisterContract了,我们让它的View接口以及Presenter接口继承自BaseContract中的即可
public interface RegisterContract { interface View extends BaseContract.View<Presenter>{//此处的presenter即为下面定义的Presenter,因为下面有继承,你懂的~ //注册成功 void registerSuccess(); }
interface Presenter extends BaseContract.Presenter { //发起一个注册 void register(String phone,String name,String password); }
} 契约(接口)有了,那么我们就应该实现它了。首先我们来实现View层,前面提到了,此例的View即我们的RegisterFragment,那么我们就让RegisterFragment实现RegisterContract.View
RegisterFragment implements RegisterContract.View,写上后面的Implements发现报红,我们必须实现RegisterContract.View里面的所有方法,其中包括了registerSuccess()以及showError(),shwLoading(),setPresenter()。很显然,除了registerSuccess()方法外,其他两个方法我们不希望它们出现在我们的RegisterFragment中,因为我们在这里实现也代表着将来的loginFragment以及各种基于Base契约开发的Fragment都必须去实现除了registerSuccess外的所有方法。so我们要将showError(),showLoading(),setPresenter()提取出来,提取到一个基本的带有presenter的fragment中去是实现,于是我们的PresenterFragment诞生了! public class PresenterFragment extends Fragment implements BaseContract.View<> 由于我们的BaseContract.View在编写的时候是带有Presenter泛型的,那么此处也应该传递一个泛型,去观察BaseContract发现此处是一个继承自BaseContract.Presenter的泛型,所以我们在PresenterFragment后面定义一个泛型<Presenter extends BaseContract.Presenter>,在BaseContract.View后面填入<Presenter> public class PresenterFragment<Presenter extends BaseContract.Presenter> extends Fragment implements BaseContract.View<Presenter> 接下里就是实现公共契约中的方法啦 showError(),shwLoading(),setPresenter()。
紧接着,我们需要一个Presenter变量,所以我们定义一个泛型为Presenter的变量mPresenter,并在setPresenter中赋值w
在此类中,除了我们应该实现的方法外,我们还没有对Presenter进行初始化,那么我们就在onAttach中对它进行初始化
@Override public void onAttach(Context context) { super.onAttach(context);
//在界面onAttach之后就触发初始化Presenter Log.d("ONATTACH", "初始化presenter"); initPresenter();
} 有初始化的调用,但是没有初始化的实现,我们需要将initPresenter实现,但是可以在此类实现吗?
不可以,此类为RegisterFragment的基类,此类取得的Presenter并非RegisterPresenter,所以我们将初始化的实现一起抛给registerFragment去实现,所以我们在initPresenter前加上abstract
那么我们也必须在类定义前加上abstract
最终的presenterFragment完成
public abstract class PresenterFragment<Presenter extends BaseContract.Presenter> extends Fragment implements BaseContract.View<Presenter> {
protected Presenter mPresenter; @Override public void onAttach(Context context) { super.onAttach(context); //在界面onAttach之后就触发初始化Presenter Log.d("ONATTACH", "初始化presenter"); initPresenter(); } /** * 初始化Presenter * @return Presenter * */ protected abstract Presenter initPresenter(); @Override public void showError(int str) { //显示错误 Application.showToast(str); } @Override public void showLoading() { //TODO 显示一个Loading } @Override public void setPresenter(Presenter presenter) { //View中赋值Presenter mPresenter = presenter; }
} 有了PresenterFragment我们就可以让RegisterFragment继承它了。
注:里面的泛型,应该传入一个BaseContract.Presenter的子类,而对于我们的RegisterFragment来说,很显然,RegisterContract.Presenter就是我们需要的Presenter
public class RegisterFragment extends PresenterFragment<RegisterContract.Presenter> implements RegisterContract.View 还记得父类抛给我们的方法吗?该实现了!
那么首先我们就要重写initPresenter,在该方法中我们该真正实现Presenter初始化了,要在其中初始化我们Register界面的Presenter,那么我们就需要一个实现类来初始化它。So我们新建一个类RegisterPresenter
public class RegisterPresenter implements RegisterContract.Presenter
注意:这里是实现了RegisterContract.Presenter中的所有契约,而RegisterContract是BaseContract的子类,所以我们需要将共有的BaseContact.Presenter中的方法以及Register.Contract里的特有方法全部实现,很明显,我们不应该这样做,所以我们再提取一个基本实现类出来,所以我们新建一个实现类BasePresenter
public class BasePresenter<T extends BaseContract.View> implements BaseContract.Presenter 注意:这里必须传入一个泛型,我们不知道子类会是个什么类型的View,但可以确定所有的View都是BaseContract.View的子类,所以我们传入T extends BaseContract.View
在BasePresenter中,我们的第一个方法就是构造方法呢。我们希望在Presenter创建的时候就将Presenter和View进行绑定,那么我们就需要在构造方法中绑定,所以我们写一个setView方法并在构造方法中调用(先定义一个泛型View的变量方便后面使用)
private T mView; public BasePresenter(T view) { setView(view); } /** * 将View与Presenter绑定 */ protected void setView(T view) { this.mView = view; //下面这个setPresenter在BaseContract中申明的方法,因为我们需要获取到View具体的类型,所以在最顶层的RegisterFragment中重写,并在这个地方调用 this.mView.setPresenter(this); }
接下来我们需要给子类提供一个getView的方法,将我们得到的View传给后面继承我们的Presenter子类。这样子类例如RegisterPresenter才可以自己得到它需要控制的View。
所以这个流程其实是 RegisterPresenter在构造函数中传入一个RegisterContract.View,然后就是调用父类BasePresenter的构造方法 --> BasePresenter构造方法中调用setView --> 在setView中将RegisterPresenter传过来的view赋值到我们最开始定义的mView并且调用mView的setPresenter方法将Presenter与View进行绑定
最后再添加上我们基类约定的方法start和destroy
所以最后的BasePresenter就是这样
public class RegisterPresenter extends BasePresenter<RegisterContract.View> implements RegisterContract.Presenter, DataSource.Callback<User> { public RegisterPresenter(RegisterContract.View view) { super(view); } @Override public void register(String phone, String name, String password) { //调用开始方法,在start中默认启动了Loading start(); //得到View接口 RegisterContract.View view = getView(); //校验 if (!checkMobile(phone)) { //提示手机号不合法 view.showError(R.string.data_account_register_invalid_parameter_mobile); } else if (name.length() < 2) { //姓名需要大于两位 view.showError(R.string.data_account_register_invalid_parameter_name); } else if (password.length() < 6) { //密码需要大于6位 view.showError(R.string.data_account_register_invalid_parameter_password); } else { //进行网络请求 //构造model,进行请求调用 RegisterModel model = new RegisterModel(phone, password, name); //进行网络请求并设置回送口为自己 AccountHelper.register(model, this); } } /** * 检查手机号是否合法 * * @param phone 手机号码 * @return 合法为true */ @Override public boolean checkMobile(String phone) { //手机号不为空并且满足相应格式 return !TextUtils.isEmpty(phone) && Pattern.matches(Common.Constance.REGES_MOBILE, phone); } @Override public void onDataLoaded(User user) { //当网络请求成功,注册好了,回送一个用户信息回来 //告知界面,注册成功 final RegisterContract.View view = getView(); if (view == null) return; //此时是从玩过回送回来的,并不保证属于主线程状态 //强制执行在主线程中 Run.onUiAsync(new Action() { @Override public void call() { //调用主界面的注册成功 view.registerSuccess(); } }); } @Override public void onDataNotAvaliable(final int strRes) { //网络请求告知注册失败 final RegisterContract.View view = getView(); if (view == null) return; //此时是从玩过回送回来的,并不保证属于主线程状态 //强制执行在主线程中 Run.onUiAsync(new Action() { @Override public void call() { //调用主界面的注册成功 view.showError(strRes); } }); } }
最后我们在RegisterPresenter中让它继承BasePresenter~
封装基本完成~有问题下面留言哦~
MVP新手,大神勿喷,如有不对的地方,欢迎指正!!