Kotlin之UI篇

作者: 负了时光不负卿 | 来源:发表于2017-09-07 00:40 被阅读1217次

    1.Android项目集成Kotlin

    当我们的IDE安装好Kotlin插件(“Kotlin”)以后,项目想集成Kotlin将是一件非常轻松的事情,我们只需要在菜单栏中选择“Tools”->"Kotlin"->"Configure Kotin in Project"即可。

    2. Kotlin-android-extensions插件减少模板代码

    用java进行Android界面开发时,最多的模板代码findviewbyid(),于是我们项目中引入了ButterKnife进行编译时注解。到了Kotlin的时候,我们需要写的代码量就更少了,我们这样做:

    在app/build.gradle中加入kotlin-android-extensions插件

    apply plugin: 'kotlin-android-extensions'
    

    于是我们就可以这样开发:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <Button
            android:id="@+id/btn_center"   //我才是重点
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true" />
    </RelativeLayout>
    
    class VideoActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_video)
           btn_center.setOnClickListener{   // 我才是重点
                toast("be Clicked!")
            }
        }
    }
    
    1. 我们不用再写findviewbyid了!!!
    2. 不用像butterknife那样
      @bindview(R.id.btn_center
      Button centerBtn

    对于控件我们只需要根据xml定义的ID直接使用!

    3.通过Anko加快界面渲染效率

    3.1 Anko的github地址

    3.2 AnKo的集成

    3.3 布局文件的两种方式的编写(添加AnKo layout的依赖库)

    java中我们可以使用xml进行代码的编写,直观简洁,但是效率不高,因为系统会进行xml解析生成对应的控件。当然,我们也可以直接用代码进行编写,效率是提高了,但阅读行和复杂性提高了。而Anko采用了一种这种的办法,定义DSL语法,用代码编写“xml”。

    模式一:

    class WebActivity : AppCompatActivity() {
    
        private lateinit var webview: WebView
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            verticalLayout {                                       // 重点在这里
                button("open Link") {
                    setOnClickListener {
                        getPagebyUrl("http://www.baidu.com")
                    }
                }
                webview = webView().lparams(matchParent, matchParent)   //同时将创建的控件全局化
            }
        }
    
                                                                      
        fun getPagebyUrl(url: String) {            // 此处后面会重点讲解优化
            Thread {
                val Page = URL(url).readText()
                runOnUiThread {
                    webview.loadData(Page, "text/html", "utf-8")
                }
            }.start()
        }
    }
    

    擦!!这么简洁的布局写在这个类里面还好,但是实际项目开发中,布局就可能比较复杂了,一个xml动不动就上百来行,全写着这个里面,类不就很臃肿了么?

    小伙子(小妹妹)如果有这想法,证明思维还是蛮成熟,AnKo也考虑到这一点,支持Activity与layout分离,我们可以这样:

    模式二:

    class MainActivityUI : AnkoComponent<MainActivity> {
    
        companion object {                    // 这里定义了控件的ID,方面在Activity中根据ID找控件
            val req_btn = 1
            val showview = 2
        }
    
        override fun createView(ui: AnkoContext<MainActivity>) = with(ui) {
            verticalLayout {
                button("请求数据") {
                    id = req_btn
                }.lparams(width = matchParent, height = wrapContent)
                textView {
                    id = showview
                }.lparams(matchParent, matchParent)
            }
        }
    }
    

    集成AnkoComponent 接口,在createView()方法中编写DSL语法,创建布局。

    接下来我们看看Activity怎么写的?

    class MainActivity : AppCompatActivity() {
    
        private lateinit var contentView: TextView
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            MainActivityUI().setContentView(this)             //将上面的布局设置给该Activity
    
            val reqBtn = find<Button>(MainActivityUI.req_btn)        // 根据ID寻找控件
            contentView = find<TextView>(MainActivityUI.showview)
    
            reqBtn.setOnClickListener {
                getPagebyUrl(“http://www.baidu.com”)
            }
        }
    
           fun getPagebyUrl(url: String) {            // 此处后面会重点讲解优化
            Thread {
                val Page = URL(url).readText()
                runOnUiThread {
                    contentView.text = Page
                }
            }.start()
        }
    }
    }
    

    3.4 anko layout中添加自定义控件

    上面的 MainActivityUI 中添加的都是系统自带的控件,实际开发过程中,单纯使用系统组件是远远不够的,于是我们开始了自定义控件。那么问题来了,自定义的控件如何添加到DSL的layout中,直接使用显然是不能够的! 不用担心,我们先看一下anko是如何让button支持DSL语法的(老规矩,点击刚刚编写的button查看源码)

    inline fun ViewManager.button(text: CharSequence?): android.widget.Button {
        return ankoView(`$$Anko$Factories$Sdk25View`.BUTTON, theme = 0) {
            setText(text)
        }
    }
    

    OMG,原来只是在ViewManager中添加一个拓展方法,通过ViewManager.ankoView()生成View对象,并初始化值(知道真相的我们,眼泪掉下来!) , 嘿嘿!接下来我们也效仿的来一套!

      1. 编写自定义控件,CircleView
      1. 创建一个View.kt文件
      1. 添加如下代码
      inline fun ViewManager.circleView(init: com.yl.testcenter.CircleView.() -> Unit) = ankoView({ CircleView(it) }, theme = 0) { init() }
    

    ps: 其中ViewManager.circleView这个扩展方法名是在layout中控件使用的名称

      1. 控件的装逼使用
    verticalLayout {
                id = container
                button("请求数据") {
                    id = req_btn
                }.lparams(width = matchParent, height = wrapContent)
                textView {
                    id = showview
                }.lparams(matchParent, wrapContent)
                circleView {                                 //  ----  我在这!! 
                    backgroundColor = Color.RED
                }
            }
    

    3.5 异步数据请求,更新UI(添加Kotlin coroutines依赖库)

    上面异步请求网络数据的方式:

    fun getPagebyUrl(url: String) {         
            Thread {
                val Page = URL(url).readText()
                runOnUiThread {
                    contentView.text = Page
                }
            }.start()
    

    上面通过异步线程切换UI线程更新控件,显然代码变现上很直接了,但如果用协程就更加明了:

     fun getInfo() {
               
                var job = async(CommonPool,start = CoroutineStart.LAZY) {     //协程任务一
                    getVideoInfo()  
                }
                launch(UI){                                                   //协程任务二
                    contentView.text = job.await()
                }
            }
    

    说明:

    1. 创建一个后台协程任务,不开启(start = CoroutineStart.LAZY声明此协程不自定开启,需要手动调用await才启动)
    2. 创建一个UI协程任务,默认为开启(没有设置start=xxx)
    3. UI协程更新控件,根据后台协程的返回值,协程不会阻塞UI线程。

    与上面相比没有线程切换的嵌套!

    1. Kotlin学习入门文档
    2. 协程相关知识
    3. 深入了解协程与channel的关系
    4. 相关框架与事例

    相关文章

      网友评论

      本文标题:Kotlin之UI篇

      本文链接:https://www.haomeiwen.com/subject/twuvjxtx.html