南京音乐推荐联合社

一款纯Kotlin编写的开源安卓应用 "Smile"

鸿洋 2019-07-10 08:38:53

本文作者


作者:王英豪

链接:

http://blog.csdn.net/yhaolpz/article/details/77922521

本文由作者投稿推送。


1
 界面


版块一:段子鸡汤



版块二:花瓣福利


     


版块三:动态搞笑图


          


2
代码片段


1.数据类



相关的数据类可以写在同一个文件中,摒弃繁多的 get/set 、toString 等方法,简洁明了!


2.属性委托



对变量 mDefaultIndex 取值将直接从 SharedPreferences 中取出,对该变量赋值将直接存入 SharedPreferences 中,没有比这更简单的写法了!



什么时候用到 mClient 什么时候初始化它的值,实现延迟初始化如此简单!


3.代码简化神器 anko



一个单词实现线程切换,无需多言,nice !!!

更多内容请前往源码查看 ~


3
如何着手Kotlin


我从对 Kotlin 一窍不通,到完整开发出此 App,共用了 9 天,其中有 4 天只在晚上开发,也就是说共计 5 天 + 4 个晚上。另外由于我对 ui 的挑剔,主题色的挑选及 logo 设计至少占去五分之一的时间。


我并不是在炫耀我的自学能力,Smile 的内容并不算多,肯定有很多大牛比我快,我想表达的是这 9 天的学习及开发过程是刀过竹解、得心应手的。所以我想把这个学习过程分享给大家,只要能帮到一个人,那就是有价值的。


首先,选择一个完整的 kotlin 文档,从第一章开始阅读,尤其是最基础的基本类型、控制流等等,千万不能跳过,边读边把知识点记录下来,期间可以阅读一些博客了解一下别人对这些知识点的理解,但是学习文档是主线,一定不能偏离。


这个过程我持续了三天,主要学习的文档为: Kotlin 语言中文站 ,期间每学到一个知识点我就记录下来,展示一部分:


  • https://www.kotlincn.net/docs/reference/basic-syntax.html



这样的内容我记录了很多,直接分享出来吧,全文链接:

https://github.com/yhaolpz/Smile/blob/master/kotlinNote.md


这个过程并不需要把所有的知识点都融汇贯通,对于你觉得比较复杂的内容,可以略读,但是不能直接跳过,你至少要知道这个知识点的存在,比如“泛型”章节我第一次阅读的时候真是云里雾里。


OK~ 这个过程完后你一定跃跃欲试了,这个委托功能真的这么酷吗!这个语法竟然这么简洁!如果这样的想法存在的话,那就说明前面的文档学习过程十分成功了!接下来就在 IDE 中尝试吧,仍然有 官方教程 为你铺路,除了创建 Kotlin 项目之外,还有 Kotlin 安卓扩展插件、Kotlin 集成第三方框架等教程,这些资料的参考顺序应该处于他人博文之前。


  • 官方教程:https://www.kotlincn.net/docs/tutorials/kotlin-android.html


然后你就可以尽情的投入 Kotlin 的怀抱了,你会遇到代码简化神器 anko ,这里推荐一份 针对 anko 的 kotlin 教程,Smile 中的泛型 preference 委托就参考自这份文档,另外 anko 中的 doAsync 和 uiThread 表示强烈鄙视 Handler 及其他所有切换线程的写法 (⊙_⊙;)


  • anko: https://github.com/Kotlin/anko/wiki

  • 针对 anko的kotlin教程: https://wangjiegulu.gitbooks.io/kotlin-for-android-developers-zh/kai_shi_shi_yong_anko.html


4
开发心得


开发过程中不要急着集成第三方库


既然你的项目已经选择了 Kotlin,为何不多体验一下它的强大呢?拿请求网络来说,以往的开发习惯可能会促使你尽快把 retrofit 集成进来,暂时搁置它吧,体会一下 kotlin 中请求网络是多么简洁,就算你仍然打算在以后的项目中使用 retrofit,这也没关系,仅这次敲几行代码体会一下也是很美好的。


Ok ~ talk is cheap ,下面看一下 Smile 中是如何请求网络数据的,比如一进去的“搞笑段子”,在其 Fragment 中是这样请求网络数据的:


doAsync {
     val data = JokeService.getData(mPage)
     uiThread {
         mLoading = false
         if (data == null) {
             showSnackbar(view as ViewGroup, "加载失败")
             return@uiThread
         }
         ...



doAsync 中会自动切换到子线程,执行完 getData() 方法后,会自动切换到 ui 线程接着执行 uiThread{ } 中的代码,来看看 getData() 方法我是怎么实现的:


fun getData(page: Int, maxResult: Int = 10): List<Joke>? {
    var forecastJsonStr: String? = null
    try {
        forecastJsonStr =
            URL(buildBaseUrl(page, maxResult)).readText()
    } catch (e: Exception) {
        return null
    }
    val data =
        Gson().fromJson(forecastJsonStr, JokeResult::class.java)
    val jokes: List<Joke> = data.showapi_res_body.contentlist
    return if (jokes.isNotEmpty()) jokes else null
}


注意,其中请求网络的代码仅仅一行:


forecastJsonStr = 
   URL(buildBaseUrl(page, maxResult)).readText()


URL 仍然是原来 java.net 包的类,但 readText() 函数正是 kotlin 的杰作, Kotlin 支持扩展函数和扩展属性,readText() 就是 URL 类的扩展函数,除此之外还扩展了 readBytes() 函数,这代表着在 kotlin 中请求网络仅仅需要一行代码,再配合强大的 doAsync 、uiThread 方法,如此简单的实现了网络请求,在写法上是不是比以往的 rxJava+retrofit 更简洁呢?

回到 getData() 方法,看最后的返回语句:


return if (jokes.isNotEmpty()) jokes else null



在 Kotlin 中,if 语句是可以有一个返回值的,但是看起来似乎可以更简单的使用 java 中的三元操作符 ? : 实现,但是三元操作符 ? : 中是不能嵌套其他代码的,下面仍然是 Smile 中的代码:


override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
       if (keyCode == KeyEvent.KEYCODE_BACK) {
           mBackPressedTime = if (mIsMenuOpen) {
               drawerLayout.closeDrawers()
               mBackPressedTime
           } else {
               System.currentTimeMillis()
           }
       }
       return true
   }


其中要实现的功能是:当按下返回键时,首先判断侧滑菜单是否打开,若打开就关闭之并执行 mBackPressedTime = mBackPressedTime 赋值语句;若处于关闭状态就执行 mBackPressedTime = System.currentTimeMillis() 赋值语句,其中使用 if 具有返回值这一特性很方便的实现了这一点。


为什么我要根据侧滑状态的打开状态分别给 mBackPressedTime 赋不同的值呢?

我想实现的功能是:在侧滑菜单关闭的状态下,快速按两次返回键,则退出应用。


通过监听 mBackPressedTime 值的变化可以实现,这里用到了 Kotlin 的属性委托:数据监听委托,来看怎么对 mBackPressedTime 实现数据变化监听的:


var mBackPressedTime by Delegates.observable(0L) {
   _, old, new ->
   if (new - old > 1000) {
       showSnackbar(coordinatorLayout,
                getString(R.string.exit_message))
   }
   if (new - old in 1..1000) {
       mDefaultIndex = mCurrentIndex
       finish()
   }
}



在声明变量的时候通过 by 关键字实现属性委托,Delegates.observable(0L) 是 Kotlin 自带的一种数据监听属性委托,代码中规定,当两次按下的时间间隔大于 1秒时显示提示框,小于 1 秒时退出应用。


最后再解释一下这段代码:


if (new - old in 1..1000) {
    mDefaultIndex = mCurrentIndex
    finish()
}


可以看到判断范围的写法与 Java 中不同, 在 Java 中我们是这样写的:


if (new - old >=1 && new - old<= 1000) 


另外就是:


 mDefaultIndex = mCurrentIndex


在本文开头贴出的代码片段中有提到过,对 mDefaultIndex 赋值意味着直接将值存入到 preference 文件中了。


这里就不再展示其他代码了,更多内容请前往源码查看 ~


通过截图无法感受到 Smile 的心意,下载试试吧,扫码下载最新版 apk


          

源码:

  • https://github.com/yhaolpz/Smile


ZZS

死板、呆萌、宅、不解人意…作为一名敲代码为生的程序员,你是不是被旁人贴过太多不属于你的标签?


1024程序员节这天,100offer给你一个撕掉标签的机会:关注100offer微信号,发送一段话/一张图/一段视频/一条语音…展示你除了敲代码以外的神技能,还有最高价值1024元的“程序员兴趣基金”等你拿!



如果你有想学习的文章直接留言,我会整理征稿。如果你有好的文章想和大家分享欢迎投稿,直接向我投递文章链接即可。

Copyright © 南京音乐推荐联合社@2017