轻松打造Kotlin MVVM框架

kotlin 被宣布成为Google Android开发的官方语言之一的时间已经过去一段时间了,作者将kotlin打造的mvvm的架构完美兼容到之前的项目中,并且使用的非常爽。中间踩过一些坑,也折腾了一段的时间,现在来讲讲是怎么操作的。

准备工作

1、Android Studio2.3.3
2、添加kotlin插件 : setting->plugin->search kotlin , install
3、添加依赖
build.gradle

ext.kotlin_version = '1.1.2-5'
·classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

app.gradle

apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'

----- 添加databinding 支持(放在android下)
 dataBinding {
        enabled = true
    }

-----  解决databinding 和kotlin的冲突 支持(放在根下)
kapt {
    generateStubs = true
}

----依赖
    compile 'org.jetbrains.kotlin:kotlin-stdlib:1.1.2-5'
    kapt 'com.android.databinding:compiler:2.3.3'//解决databinding 和kotlin的冲突 支持

// retfofit
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
    compile 'com.squareup.okhttp3:logging-interceptor:3.8.0'
//添加rxjava 依赖 根据自己的版本处理 
我使用的是1.0+版本

4、分包
···
-base
-view
-modle
-viewmodle
-serviceapi
···

打造BaseView

BaseActivity.kt

abstract class BaseAct<B : android.databinding.ViewDataBinding, V : BaseViewModle<B>> : AppCompatActivity() {

    protected lateinit var b: B
    protected lateinit var viewModle: V


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

        b = android.databinding.DataBindingUtil.setContentView<B>(this, getLayoutResource())
        viewModle = TUtil.getT(this, 1)!!;
        viewModle.init(this, b, this.baseContext)
    }


    inline fun <reified T : Activity> Activity.gotoActivity() {
        val intent = Intent(this, T::class.java)
        this.startActivity(intent)
    }


    /**
     * 是否支持滑动返回

     * @return
     */
    protected fun supportSlideBack(): Boolean {
        return true
    }


    private var mSwipeWindowHelper: SwipeWindowHelper? = null

    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
        if (!supportSlideBack()) {
            return super.dispatchTouchEvent(ev)
        }

        if (mSwipeWindowHelper == null) {
            mSwipeWindowHelper = SwipeWindowHelper(window)
        }
        try {
            return mSwipeWindowHelper!!.processTouchEvent(ev) || super.dispatchTouchEvent(ev)
        } catch (_e: Exception) {
            _e.printStackTrace()
        }

        return false
    }


    /**
     * menu 点击事件
     */
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            android.R.id.home// 点击返回图标事件
            -> onBackPressed()
            else -> menuItemSelected(item)
        }
        return super.onOptionsItemSelected(item)
    }


    fun menuItemSelected(item: MenuItem) {
    }

    protected abstract fun getLayoutResource(): Int

    override fun onResume() {
        super<AppCompatActivity>.onResume()
        android.util.Log.v(tag(), tag() + "::onResume()")
        viewModle.onResume()
    }

    private fun tag(): String? {
        return this.localClassName
    }

    override fun onPause() {
        super<AppCompatActivity>.onPause()
        viewModle.onPause()
        android.util.Log.v(tag(), tag() + "::onPause()")
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        viewModle.onActivityResult(requestCode, resultCode, data)
    }


    override fun onDestroy() {
        android.util.Log.v(tag(), tag() + "::onDestory()")
        super<AppCompatActivity>.onDestroy()
        viewModle.onDestory()
    }

}

BaseFragment.kt

abstract class BaseFrag<B : ViewDataBinding, V : BaseViewModle<B>> : Fragment(), java.io.Serializable {

    override fun onCreateView(inflater: android.view.LayoutInflater?, container: android.view.ViewGroup?, savedInstanceState: android.os.Bundle?): android.view.View? {
        return inflater!!.inflate(getLayoutResource(), container, false)
    }

    protected abstract fun getLayoutResource(): Int
    protected lateinit var viewModle: V
    protected lateinit var b: B
    override fun onViewCreated(view: android.view.View?, savedInstanceState: android.os.Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        b = android.databinding.DataBindingUtil.bind<B>(view)
        viewModle = TUtil.getT(this, 1)!!;
        viewModle.init(activity as AppCompatActivity, b, context)
        initView()
    }

    abstract fun initView()




    inline fun <reified T : Activity> Activity.gotoActivity() {
        val intent = Intent(this, T::class.java)
        activity.startActivity(intent)
    }

    private fun tag(): String? {
        return this.javaClass.simpleName
    }


    protected fun toast(text: String) {
        android.widget.Toast.makeText(context, text, android.widget.Toast.LENGTH_SHORT).show()
    }


    override fun onResume() {
        super.onResume()
        viewModle.onResume()
    }

    override fun onDestroy() {
        super.onDestroy()
        viewModle.onDestory()
    }

    override fun onPause() {
        super.onPause()
        viewModle.onPause()
    }


}

BaseViewModle.kt

abstract class BaseViewModle<B : ViewDataBinding> : ViewModleRecycle {
    protected lateinit var b: B
    protected lateinit var act: AppCompatActivity
    protected lateinit var context: Context


    companion object {

    }

    protected fun toast(text: String) {
        Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
    }

    fun initToolBar(toolbar: Toolbar, title: String) {
        toolbar.setTitle(title)
        toolbar.setTitleTextColor(context.resources.getColor(R.color.colorAccent))
        act.setSupportActionBar(toolbar)
        act.supportActionBar!!.setDisplayHomeAsUpEnabled(false)
    }


    fun init(act: AppCompatActivity, b: B, c: Context) {
        this.b = b
        this.act = act
        context = c
        initUI()
        initNet()
    }

    inline fun <reified T : Activity> Activity.gotoActivity() {
        var intent = Intent(this, T::class.java)
        act.startActivity(intent)
    }


    abstract fun initUI()
    abstract fun initNet()


    fun clear() {

    }

    override fun onResume() {

    }

    override fun onPause() {

    }

    override fun onDestory() {

    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

    }

}

搞定!!!

使用

  • 干货的来源api

view

class GirlsFragment : BaseFrag<FragmentGirlsBinding, GrilsViewModle>() {
    override fun initView() {
        initToolBar(b!!.toobar, "妹纸")
    }

    companion object {
        fun newInstance(): GirlsFragment {
            val fragment = GirlsFragment()
            return fragment
        }
    }

    override fun getLayoutResource(): Int {
        return R.layout.fragment_girls
    }


}

viewmodle


/**
 * Created by apanda on 2017/6/13.
 */
class GrilsViewModle : BaseViewModle<FragmentGirlsBinding>(), BaseBindingItemPresenter<Result> {
    public override fun onItemClick(position: Int, itemData: Result) {
        toast("itemClicked")
    }


    private var adapter: SingleTypeBindingAdapter? = null
    override fun initUI() {
        adapter = SingleTypeBindingAdapter(context, null, R.layout.item_girls)
        adapter!!.setItemPresenter(this)
        b!!.recylerview.adapter = adapter
        b!!.recylerview.layoutManager = GridLayoutManager(context, 2)
    }


    override fun initNet() {
        val api = Service.Factory.create()
        api.getData("福利", 20, 1)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe({ result ->
                    adapter!!.refresh(result.results)
                }, { _ ->
                })
    }
}

api

interface Service {
    @GET("api/data/{type}/{pageSize}/{pageNumber}")
    fun getData(@Path("type") type: String,
                @Path("pageSize") pageSize: Int,
                @Path("pageNumber") pageNumber: Int): Observable<BasePojo<List<Result>>>


    companion object Factory {
        fun create(): Service {

            val logging = HttpLoggingInterceptor()
            logging.level = HttpLoggingInterceptor.Level.BASIC
            val client = OkHttpClient.Builder()
                    .addInterceptor(logging)
                    .build()

            val retrofit = Retrofit
                    .Builder()
                    .client(client)
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .addConverterFactory(GsonConverterFactory.create())
                    .baseUrl("http://gank.io/")
                    .build()

            return retrofit.create(Service::class.java)
        }
    }

}

下篇文章将会使用该框架实现 钉钉在聊天框 粘贴html地址,实现内容预览的功能。
另外自己使用的框架中还实现了一个上下拉刷新加载的BaseViewLoadingDataModle,有需要的童鞋,可以跟我交流。

项目地址: http://git.oschina.net/apandas/kotlinframe

Copyright (c) 2017 Copyright Holder All Rights Reserved.
原创文章,转载需说明出处,版权归作者所有。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,392评论 25 707
  • afinalAfinal是一个android的ioc,orm框架 https://github.com/yangf...
    passiontim阅读 15,394评论 2 45
  • Google在今年的IO大会上宣布,将Android开发的官方语言更换为Kotlin,作为跟着Google玩儿An...
    蓝灰_q阅读 76,745评论 31 489
  • 年轮碾过岁月的尘埃,留下那些清浅的印记在心底,恣意的漾泛,始终执着于恍若初见的意念,在灵魂深处珊澜,一字一句的忆起...
    古城苍狼阅读 648评论 10 13
  • 夜幕降临时,华灯初上,苍茫的天际上飘着一朵祥云,整个亚丁街角静谧安详。
    大王的小妖姬阅读 258评论 0 2