Kotlin如何實現MVP架構

airland發表於2021-09-09

最近MVP火的不要不要的,我也來湊盤熱鬧,其實MVP的文章看了好多了,但是一直還停留在概念階段,這幾天對Google Android的todoapp的程式碼進行了解析,然後使用Kotlin翻譯了一下,算了對MVP有了一個大概的認識,那麼MVP究竟是什麼,網上一搜一大堆,我就把名字複述一下,MVP的全稱為Model-View-Presenter,即模型-檢視-協調器(主持者),最重要的功能還是解耦,程式碼也更容易擴充套件和維護,下面我們還是來看具體的例子:demo還是做一個乾貨的列表頁:

先看目錄結構,

圖片描述

Paste_Image.png

這裡單獨建了個MVP的包,base中定義了兩個介面BaseViewBasePresenter

BaseView

package com.vslimit.kotlindemo.mvp.base

/**
 * Created by vslimit on 17/1/6.
 */
interface BaseView {
    fun setPresenter(presenter: T)
}

BasePresenter

package com.vslimit.kotlindemo.mvp.base

/**
 * Created by vslimit on 17/1/6.
 */
interface BasePresenter {
    fun start()
}

實現Contract

GanksContract

package com.vslimit.kotlindemo.mvp.ganks

import com.vslimit.kotlindemo.model.Gank
import com.vslimit.kotlindemo.mvp.base.BasePresenter
import com.vslimit.kotlindemo.mvp.base.BaseView

/**
 * Created by vslimit on 17/1/10.
 */
interface GanksContract {

    interface View : BaseView {
        fun showGanks(ganks: List)
        fun showTip(message:String)
        fun showLoading()
        fun hideLoading()
    }

    interface Presenter : BasePresenter {
        fun loadGanks()
    }

}

介面中定義了需要實現想要展示的View,而Presenter處理互動,下面我們來看具體的實現類:

GanksPresenter

package com.vslimit.kotlindemo.mvp.ganks

import com.vslimit.kotlindemo.model.Gank
import com.vslimit.kotlindemo.model.GankListResult
import com.vslimit.kotlindemo.service.RestfulService
import com.vslimit.kotlindemo.service.ServiceFactory
import rx.Subscriber
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers

/**
 * Created by vslimit on 17/1/11.
 */
class GanksPresenter : GanksContract.Presenter {

    var mGanksView: GanksContract.View? = null

    constructor(ganksView: GanksContract.View) {
        mGanksView = checkNotNull(ganksView)
        mGanksView!!.setPresenter(this)
    }

    override fun loadGanks() {
        mGanksView!!.showLoading()
        val service = ServiceFactory.createRetrofitService(RestfulService::class.java, RestfulService.GANK_SERVICE_ENDPOINT)
        service.loadGanks().subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : Subscriber() {
            override fun onCompleted() {
                mGanksView!!.hideLoading()
            }

            override fun onError(e: Throwable) {
                mGanksView!!.showTip(e.toString())
            }

            override fun onNext(result: GankListResult) {
                processGanks(result.results)
            }
        })
    }

    override fun start() {
        loadGanks()
    }

    fun processGanks(ganks: List) {
        mGanksView!!.showGanks(ganks)
    }


}

這裡只實現了一個互動,即從服務端獲取ganks資料,獲取資料的實現請參考之前的文章,既然資料OK了,那我們就來看看View是如何展示的:

GanksFragment

package com.vslimit.kotlindemo.mvp.ganks

import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import android.view.View
import com.vslimit.kotlindemo.R
import com.vslimit.kotlindemo.adapter.BaseAdapter
import com.vslimit.kotlindemo.extensions.loading
import com.vslimit.kotlindemo.fragment.BaseFragment
import com.vslimit.kotlindemo.model.Gank
import com.vslimit.kotlindemo.util.Const
import kotlinx.android.synthetic.main.fragment_ganks.*
import kotlinx.android.synthetic.main.item_list_gank.view.*
import org.jetbrains.anko.info
import org.jetbrains.anko.onClick
import org.jetbrains.anko.support.v4.act
import org.jetbrains.anko.support.v4.toast

/**
 * Created by vslimit on 17/1/10.
 */
class GanksFragment : BaseFragment(), GanksContract.View {
    override val layoutResourceId: Int = R.layout.fragment_ganks

    var mPresenter: GanksContract.Presenter? = null
    var adapter: BaseAdapter? = null

    companion object {
        fun getInstance(): GanksFragment {
            return GanksFragment()
        }
    }

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val layoutManager: LinearLayoutManager = LinearLayoutManager(act)
        gankListRv.layoutManager = layoutManager
        adapter = BaseAdapter(R.layout.item_list_gank, arrayListOf()) { view: View, item: Gank ->
            view.item_gank_title.text = item.desc
            view.item_gank_who.text = item.who
            view.item_gank_date.text = item.publishedAt
            view.onClick {
                showTip("")
            }

        }
        gankListRv.adapter = adapter

    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun hideLoading() {
        loading(Const.HIDE)
    }

    override fun showLoading() {
        loading(Const.SHOW)

    }

    override fun showTip(message: String) {
        toast(message)
    }

    override fun setPresenter(presenter: GanksContract.Presenter) {
        mPresenter = checkNotNull(presenter)
        info("demo")
    }

    override fun onResume() {
        super.onResume()
        mPresenter?.start()
    }

    override fun showGanks(ganks: List) {
        adapter!!.items = ganks
        adapter!!.notifyDataSetChanged()
    }

}

在這個fragment同樣使用了之前文章封裝的通用RecycleView.Adapter,有興趣的朋友可以回頭看看之前的文章,最後我們來看看Activity都實現了什麼:

GanksActivity

package com.vslimit.kotlindemo.mvp.ganks

import android.os.Bundle
import android.support.design.widget.NavigationView
import android.support.v4.view.GravityCompat
import android.support.v4.widget.DrawerLayout
import android.view.MenuItem
import com.vslimit.kotlindemo.R
import com.vslimit.kotlindemo.activity.BaseActivity
import com.vslimit.kotlindemo.ui.DrawerLayoutManager
import org.jetbrains.anko.find

/**
 * Created by vslimit on 17/1/10.
 */
class GanksActivity(override val layoutResourceId: Int = R.layout.activity_tasks) :BaseActivity(),DrawerLayoutManager{

    override val drawerLayout by lazy { find(R.id.drawer_layout) }
    val navigationView by lazy { find(R.id.nav_view) }
    var mGanksPresenter: GanksPresenter? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setupDrawerContent(navigationView, R.id.drawLayoutTask)

        val ganksFragment = GanksFragment.getInstance()
        if (savedInstanceState == null) {
            initFragment(ganksFragment)
        }
        mGanksPresenter = GanksPresenter(ganksFragment)

    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            android.R.id.home -> {
                drawerLayout.openDrawer(GravityCompat.START)
                return true
            }
        }
        return super.onOptionsItemSelected(item)
    }

}

至此,基於Kotlin的MVP已經實現,從程式碼中不難看出,modelview實現瞭解耦,那麼問題來了,是不是MVP適用於所有場景,就我個人的感覺,如果頁面UI設計複雜,互動多,那應該使用MVP,這樣,能達到解耦的目的和效果,並且便於以後的維護和擴充套件,但是,如果只是單一的互動,比如登入或者像本文的只是個列表展示或詳情,那就直接用ActivityFragment即可,畢竟殺雞用牛刀確實有點小題大做的感覺;至於說專案中究竟用MVC還是MVP要看具體場景以及個人喜好。

哦,對了,效果圖(懶得截圖,就使用之前的了,不過點選按鈕在側滑選單中,列表效果是一樣的):

圖片描述

原文連結:http://www.apkbus.com/blog-847095-68637.html

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/964/viewspace-2813741/,如需轉載,請註明出處,否則將追究法律責任。

相關文章