一、MVP框架设计模式基本认识
(一) 基本概念
MVP是模型(Model)、视图(View)、主持人(Presenter)的缩写,分别代表项目中3个不同的模块
1.模型(Model):负责处理数据的加载或者存储,比如从网络或本地数据库获取数据等;
2.视图(View):负责界面数据的展示,与用户进行交互;
3.主持人(Presenter):相当于协调者,是模型与视图之间的桥梁,将模型与视图分离开来
MVP模式的整个核心过程
(二)开发中MVP框架模式的设计
1.一般MVP设计都是新建一个mvp的包名,里面分别在建model、view、presenter包名,mvp包名和页面的包名ui的activity和fragment是分开的
mvp放到activity包名下就显得很难看
2.mvp包下的文件设计
(1) model包名下的类
设计数据处理的逻辑,这里一般还设计一个内部接口类,让presenter对象可以设置监听并得到数据。因为好多数据处理都是需要子线程的,不能马上就返回数据
(2) view包名下的类
设计视图中需要的逻辑,都是接口方法,该类也是接口类。这个接口类也是需要固定的Activity页面来实现,并重写里面的方法,在方法里面写入页面的操作,这些重写方法能否得到回调都是Presenter对象来控制的,一般都是数据或逻辑处理完后再判断相应的方法回调
(3) presenter包名下的类
设计activity和model的交互,既要有View对象,也要有model对象
Activity中把数据相关的逻辑操作都扔给了Presenter去做,View只负责处理与用户界面操作。而Presenter调用Model处理完数据之后,再通过View接口方法更新View显示的信息
二.下面看下MVP模式在具体项目中的使用
登录页面:
点击登录并验证成功后跳转到用户界面
(一) 详细设计过程和思路
1.写布局页面,两个EditText和一个按钮
2.设计用户的基数User,设置用户名和密码的get和set方法
3.设计mvp中的View接口类LoginView,接口里面定义方法:获得用户的账号、获得用户的密码、登录、显示网络异常、登录验证错误这五个方法
代码:
/**
* mvp中的View的定义
* 用户登录界面用户的操作行为的定义
*/
interface LoginView {
fun getAccount(): String//获取用户的账号,返回账号
fun getPassword(): String//获取用户的莫玛,返回密码
fun loginSuccess(user: User) //登录的实现,需要传入用户对象
fun showNetworkError() //显示网络异常
fun showVerifyFailed() //信息验证失败,账号或密码有误
}
4.设计mvp中的model类LoginModel,这个类需要验证用户的账号和密码是否正确。并创建一个内部接口,让数据处理完成后返回时间,因为一般网络数据都是在子线程中完成,不能马上返回数据
/**
* MVP中的model数据处理类
* 这里处理登录时的数据
*/
class LoginModel {
/**
* 处理登录业务并返回结果
*/
fun login(name: String, password: String, onLoginResultListener: OnLoginResultListener) {
//一般登录都是请求服务器,验证
//这里就简单一点,大家别介意
if ("liwen" == name && "123456" == password) {
onLoginResultListener.loginSuccess(User(name, password)) //登录成功,给他返回用户对象
} else {
onLoginResultListener.loginFailure() //登录失败
}
}
//回调接口
interface OnLoginResultListener {
fun loginSuccess(user: User) //登录成功后回调的方法,返回User对象
fun loginFailure() //登录失败后回掉的方法
}
}
5.设计mvp中的Presenter类LoginPresenter,实现View和Model的交换,重点理解
/**
* mvp中Presenter中的设计
* 也是比较难,需要重点理解的一个
* presenter是主持人的意思,view和model的中间者
* 需要同时要有View的对象和Model的对象!一般做法是:在构造方法中创建model对象,并创建一个方法绑定View接口
* 这里可以发现数据处理后或者逻辑判断完后都是给mvp中的View对象来做操作的!
*/
class LoginPresenter {
/**
* 登录业务实现者,数据处理的操作者
*/
private val mLoginModel: LoginModel
/**
* 在构造方法中实例化model对象
*/
init {
mLoginModel = LoginModel()
}
//视图接口对象
private lateinit var mLoginView: LoginView
/**
* 绑定View 的方法
*
* @param loginView
*/
fun bind(loginView: MyActivity) {
mLoginView = loginView
}
/**
* 登录业务
*/
fun login() {
val account: String = mLoginView!!.getAccount().toString()
val password: String = mLoginView!!.getPassword().toString()
Log.e("TAG", "account:$account,password$password")
if (checkParameter(account, password)) {
doSomePrepare()
//登录 ,需要处理数据,所有要在model中执行
mLoginModel.login(account, password, object : OnLoginResultListener {
//登录成功的回调方法
override fun loginSuccess(user: User) {
mLoginView!!.loginSuccess(user) //在给视图页面返回User对象
}
//登录失败的回调方法
override fun loginFailure() {
mLoginView!!.showVerifyFailed() //在给视图页面返回验证失败的结果
}
})
}
}
/**
* 做一些准备
*/
private fun doSomePrepare() {
//这里可以设置按钮不可点击!否则一直点击登录也是不好
}
/**
* 检测参数是否是否为空~~~
*
* @param account
* @param password
* @return
*/
private fun checkParameter(account: String, password: String): Boolean {
if (TextUtils.isEmpty(account) or TextUtils.isEmpty(password)) {
mLoginView!!.showVerifyFailed() //提示错误
return false
} else if (!checkNetwork()) {
mLoginView!!.showNetworkError() //提示网络错误
return false
}
return true
}
/**
* 检测网络是否可用
*/
fun checkNetwork(): Boolean {
return true //先显示可以联网,实际中要用代码判断
}
}
上面有些方法是可以不写的,比如检查网络或做一些准备,但是实际程序中,是要做很多判断的,这里就随便啰嗦几句
6.主活动Activity,重点理解
/**
* 登录程序示例的Activity
* 这里需要创建presenter对象,presenter对象中是有view对象和model对象的!
*/
class MyActivity : AppCompatActivity(), LoginView, OnClickListener {
private lateinit var loginPresenter: LoginPresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initView()
}
/**
* 初始化数据
*/
private fun initView() {
btn_login.setOnClickListener(this)
loginPresenter = LoginPresenter()
//绑定View和Presenter,因为这个Activity已经实现了接口,已经包含了View对象
loginPresenter.bind(this)
}
/**
* 登录按钮的监听方法
* 这里要做后台数据的处理,需要用到Presenter
*/
override fun onClick(v: View) {
if (v.id == R.id.btn_login) {
loginPresenter.login()
}
}
/**
* 下面五个方法都是实现LoginView后要是实现的方法
*/
override fun getAccount(): String {
return et_name.text.toString()
}
override fun getPassword(): String {
return et_password.text.toString()
}
override fun loginSuccess(user: User) {
//登录成功后,一般是实现页面的跳转
Toast.makeText(this@MyActivity, user.name + "登录成功", Toast.LENGTH_SHORT).show();
//页面跳转
val intent = Intent(this@MyActivity, UserInfoActivity::class.java)
intent.putExtra("user", user)
startActivity(intent)
}
override fun showNetworkError() {
Toast.makeText(this, "当前网络不可用", Toast.LENGTH_SHORT).show()
}
override fun showVerifyFailed() {
Toast.makeText(this, "输入的用户名或密码有误", Toast.LENGTH_SHORT).show()
}
}
上面可以看到在初始化时就创建loginPresenter对象,而loginPresenter的构造方法中也是创建了loginModel对象,Activity实现了LoginView,就相当于有了LoginView对象,这是只需要用loginPresenter绑定LoginView,这时MVP的model、view、presenter三者都已经实例化,并关联在一起了
上面语句loginPresenter绑定LoginView的是:loginPresenter.bind(this);绑定Avtivity,也是绑定View!loginPresenter中调用LoginView的方法都是在Activity中执行的
程序运行后的显示:
下载源码github地址:loginMVP(kotlin),没学过kotlin先看一下java代码:loginMVP(java)