创建新的Activity

如何使用BaseActivity来创建一个新的Activity, 并且遵循MVP设计模式?

抽象

首先需要了解清楚需求, 确认页面有哪些行为, 抽象出View的接口以及Model的接口. 下面以登录界面为例. 一个基本登录界面应该包含用户名和密码输入框, 登录按钮, 以及注册和忘记密码的入口. 那么我们应该如何抽象出View呢? 从登录流程入手(从程序的角度出发):

1. Activity检测到用户点击了登录按钮
2. Activity转发事件到Presenter
3. Presenter简单验证用户名和密码是否合法
  3.1. Presenter通知View用户名或者密码不合法
4. Presenter通知View显示加载框
5. Presenter调用Model的登录接口
6. Model返回登录结果给Presenter
7. Presenter解析结果
  7.1. Presenter通知View隐藏加载框
  7.2. Presenter通知View登录成功
  7.3. Presenter通知View错误消息

既然我们使用的是MVP被动视图, 那么我们应该抽象的是View的被动行为, 即Presenter告诉View要做的行为. 从上述流程我们可以提取出这么几个行为: 提示用户名不合法, 提示密码不合法, 显示加载框, 隐藏加载框, 处理登录成功, 显示登录接口返回的错误. 但是BaseView已经包含了显示加载框, 隐藏加载框以及显示一些错误消息, 那么对应到代码则应该是这样:

public interface LoginView extends BaseView {
    void loginComplete();
    void usernameError();
    void passwordError();
}

同时我们也能顺水推舟找出Presenter里的公共方法以及Model中的接口.

public class LoginPresenter extends BasePresenter<LoginView> {
    public void login(String username, String password){}
}
public interface LoginManager {
   void login(String username, String password); 
}

实现

现在有了View Presenter和Model之后我们就可以开始创建界面了, 首先声明一个LoginActivity, 如下所示:

public class LoginActivity extends BaseActivity<LoginView, LoginPresenter>
        implements LoginView {

然后实现BaseActivity中的三个抽象方法:

    @Override
    protected int getLayoutId() {
        return R.layout.activity_login;
    }

    @Override
    protected void init(Bundle savedInstanceState) {
        initNav(); //初始化标题栏
    }

    @Override
    protected LoginPresenter createPresenter() {
        return new LoginPresenter();
    }

接着实现LoginView中的几个方法:

    @Override
    public void loginComplete() {
        startActivity(MainActivity.getLaunchIntent(this)); //跳转至主页
        finish();
    }

    @Override
    public void usernameError() {
        showToast("请输入正确的手机号"); //建议使用string资源
    }

    @Override
    public void passwordError() {
        showToast("请输入6-16位密码"); //建议使用string资源
    }

Activity中的功能基本已经实现. 另外在这里建议每个Activity都提供以下方法:

public static Intent getLaunchIntent(Context context, int flag) {
    Intent intent = new Intent(context, LoginActivity.class);
    intent.putExtra(flag, EXTRA_FLAG);
    return intent;
}

在其他界面需要跳转至LoginActivity的时候就只需要:

startActivity(LoginActivity.getLaunchIntent(this, 1));

这样写的好处是其他界面里不用去组装intent, 也不必在意intent里extra的名字, 特别是很多界面需要跳至这个界面的时候, 能减少很多重复代码, EXTRA这种静态常量也只需要在一个类的内部维护就可以了.

接下来看Presenter:

    public void login(String username, String password) {
        if (!checkUserInput(username, password)) return;

        view.showLoading();
        manager.login(username, password)
                .compose(new ResponseTransformer<>(this.<BaseData> bindLifeCycle()))
                .subscribe(new ResponseSubscriber<BaseData>() {
                    @Override public void success(BaseData baseData) {
                        // 此处意味着登录成功, 其他错误情况已自动处理
                        view.loginComplete();
                    }
                });

    }

    private boolean checkUserInput(String username, String password) {
        if (!ValidationUtil.validatePhone(username)) {
            view.usernameError();
            return false;
        }

        if (stringIsNull(password) || password.length() < 6 || password.length() > 16) {
            view.passwordError();
            return false;
        }

        return true;
    }

Presenter的代码比较简单, 主要就是做了一次用户名和密码的验证, 然后发送网络请求, 需要注意的是发送网络请求的代码使用了Retrofit2与RxJava. 隐藏加载框, 错误提示等操作都在ResponseSubscriber类里面做了, 后续会提到这一块.

Model里则是直接使用的Retrofit2, 这里就不赘述了.

总结

创建一个新Activity或者Fragment可以遵循以下步骤:

  • 分析需求! 重要

  • 抽象View, Model

  • 创建Presenter, 填入View的泛型并提供公共方法

  • 创建新的Activity并继承自BaseActivity

  • 填入相应的View与Presenter的泛型

  • 实现BaseActivity的三个抽象方法

  • 实现先前抽象出来的View接口

如果一个界面没有什么逻辑只是单纯的显示应该怎么做?

  • 创建新的Activity并继承自BaseActivity

  • 实现BaseActivity的三个抽象方法并在createPresenter()方法中返回null

单纯的继承BaseActivity与继承其他Activity一样并无太大区别, 只需要实现三个抽象方法即可, 其他用法都一样. 但是如果一旦使用了presenter变量则会抛出空指针异常.

Last updated