这篇重点贴下 Kotlin Mvp 的代码,Kotlin 中 MVVM已经相当盛行了,但处理业务逻辑较多的场景中 Mvp 还是有需要的,废话不多说,直接上代码
首先定义统一的 IView 接口,定义 BaseView 中的行为,新建 BaseView.kt 文件
interface IView {
fun showToast(msg: String)
fun showLoading(color: Int = Color.BLUE, tip: String = " 正在加载中 ... ")
fun hideLoading()
fun onError(throwable: Throwable)
}
接着定义 IPresenter 接口,当然新建 BasePresenter.kt 文件最好
interface IPresenter
这时就需要将 Presenter 与 View 相关联了,首先 Presenter 中引入 View
interface IPresenter {
val mView: View
}
接着 View 中添加 Presenter
interface IView {
val mPresenter: Presenter
// ...
}
上述的 IPresenter 会出错,因为 IPresenter 有泛型,Kotlin 是真泛型的语言,所以修改为如下
interface IView>>{
val mPresenter: Presenter
// ...
}
这里同样的 IView 也需要将泛型添加进去。这样一改,IPresenter 类中也需要将泛型补全了,代码如下
interface IPresenter>> {
val mView: View
}
同样的方式补全后,我们发现两个类都出错了!!!提示 Type argument is not within its bounds。不要慌,这时了解 Kotlin 的都知道,此时协变和逆变就该上场了
如果你的类是将泛型作为内部方法的返回,那么可以用 out;如果你的类是将泛型对象作为函数的参数,那么可以用 in;这里不多解释,两个类都加上 out 修饰即可。
interface IView>>
interface IPresenter>>
接口定义完成,接下来定义基类 BaseView、BasePresenter。先定义 abstract 的 BasePresenter
abstract class BasePresenter>> : IPresenter {
override lateinit var mView: View
}
这里使用 lateinit 延迟初始化,当然编译器不能通过,提示 Type parameter View is declared as 'out' but occurs in 'invariant' position in type View。简单说就是被声明为out的类型T不能出现在输入的位置,而我们知道如何使用,所以需要 @UnsafeVariance 来修饰,意思是告诉编译器我知道,你不要管我知道怎么搞
abstract class BasePresenter>> : IPresenter {
override lateinit var mView: @UnsafeVariance View
}
BaseView 写起来就简单多了
interface BaseView>> : IView {
}
View 和 Presenter 书写完成后,自然是封装 BaseActivity 了,我这里定义为 BaseMvpActivity,抽象类,继承自 AppCompatActivity,当然你也可以继承自其他的初始 Activity。
abstract class BaseMvpActivity>> :
AppCompatActivity(), BaseView {
override val mPresenter: Presenter
get() = TODO("Not yet implemented")
override fun showToast(msg: String) {
TODO("Not yet implemented")
}
override fun showLoading(color: Int, tip: String) {
TODO("Not yet implemented")
}
override fun hideLoading() {
TODO("Not yet implemented")
}
override fun onError(throwable: Throwable) {
TODO("Not yet implemented")
}
}
将 IView 中方法都实现了,这里重点关注下 Presenter 的初始化,以及 Presenter 与 View 的关联。通常会通过抽象方法交由子类去实现,而这里用到了泛型,所以我们通过反射去初始化,调用 findPresenterClass 得到 Presenter 的类型
private fun findPresenterClass(): Class {
var thisClass: Class<*> = this.javaClass
while (true) {
(thisClass.genericSuperclass as? ParameterizedType)?.actualTypeArguments?.firstOrNull()
?.let {
return it as Class
}
?: run {
thisClass = thisClass.superclass ?: throw IllegalArgumentException()
}
}
}
自然我们在 Activity 初始化时调用,代码修改如下,同事将 View 与 Presenter 想关联
// override val mPresenter: Presenter
// get() = TODO("Not yet implemented")
final override val mPresenter: Presenter
init {
mPresenter = findPresenterClass().newInstance()
mPresenter.mView = this
}
使用
如在 MainActivity 中使用,定义 MainPresenter ,MainView ,这里简单的一个 login 测试方法
class MainPresenter : BasePresenter() {
fun login(hello: String) {
mView.onLoginSuccess("$hello $hello")
}
}
interface MainView : BaseView {
fun onLoginSuccess(s: String)
}
MainActivity 代码,直接贴出来,同样如此简单
class MainActivity : BaseMvpActivity(), MainView {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val hello = findViewById(R.id.btn)
hello.setOnClickListener {
mPresenter.login(hello.text.toString())
}
}
override fun onLoginSuccess(s: String) {
Log.d("Mvp", s);
}
}
这样一个最简洁的 Kotlin Mvp 就实现了