一觉醒来Kotlin成了Android的新宠儿【附代码】

简介:

前言

凌晨的谷歌I/O 2017开发者大会直播你有没有观看呢?安卓虽然已经成为了移动设备第一操作系统,全球采用Android操作系统的激活设备超过了20亿台。不过对于谷歌来说,需要依靠java来做安卓开发一直是一个心病,因为oracle公司因为java和谷歌在安卓系统上的诉讼搞得心力憔悴。

现在好了,谷歌官方正式支持Kotlin,成为官方认可的安卓开发第一语言,而且从Android Studio 3.0开始,将直接集成Kotlin而无需安装任何的插件。

正文

开始做安卓UI开发一直是使用XML文件来实现。虽然理论上,UI可以使用Java语言来实现,但并没有太多的用处。不久前,JetBrains推出了Kotlin,一种面向JVM的现代语言,可以很好的实现安卓UI。

Jetbrains宣称Anko是Android中更快,更轻松的开发风格。Kotlin提供Anko库来作为DSL(领域专用语言)去设计安卓界面,一个简单的例子:

下面的界面由一个图片和一个按钮组成:

使用Anko实现如下:

 
 
  1. verticalLayout{ 
  2.        imageView(R.drawable.anko_logo). 
  3.                 lparams(width= matchParent) { 
  4.                     padding = dip(20) 
  5.                     margin = dip(15) 
  6.         } 
  7.         button("Tap to Like") { 
  8.                 onClick { toast("Thanks for the love!") } 
  9.         } 
  10.     }  

我们定义了一个垂直的线性布局作为容器包含图片和按钮,使用lparams定义了布局的位置信息,由Kotlin的内联函数也实现了按钮的点击事件。

使用Anko的优点:

  • 我们可以将UI布局嵌入到代码中,从而使其类型安全。
  • 由于我们不用XML编写,所以它增加了效率,因为在分析XML浪费CPU时间。
  • 在UI的程序化转换之后,我们可以将Anko DSL片段放入一个函数中。这样便于代码重用。
  • 显然,代码更简洁,可读和可掌握性更高。

现在我们使用Anko Layout和Kotlin构建一个to-do app,来列出我们今天需要做的事。

你可以在GitHub上找到这个项目 to-do app

将Anko库添加到Android Studio:

在streamline-android-java-code-with-kotlin去学习如何添加Kotlin到你的安卓项目中,有了Kotlin,我们需要添加Anko依赖在app/build.gradle中,这样我们就可以顺利编译项目了。

 
 
  1. compile [size=1em]'org.jetbrains.anko:anko-sdk15:0.8.3' 
  2.  
  3. // sdk19,21,23 也可以使用 

可以根据你项目的minSdkVersion来添加这个依赖,上面的例子说明15<=minSdkVersion<19,你可以在Anko的GitHub库中找到自己需要的其他Anko依赖库。

我们准备使用下面的依赖库:

 
 
  1. compile 'org.jetbrains.anko:anko-design:0.8.3' 
  2.  
  3. compile 'org.jetbrains.anko:anko-appcompat-v7:0.8.3' 

在Activity中调用Anko布局:

我们不再使用XML来写布局文件,所以我们不需要XML View,所以也不需要findViewById()方法了。这里我们假设我们的Anko布局类为MainUI,然后我们可以开始写我们的activit内容:

 
 
  1. var ui =MainUI()           //MainUI类代替了XML布局 
  2.  
  3. ui.setContentView(this)   //this代表Activity类 

现在我们创建一个Kotlin文件MainActivity.kt,写上如下代码:

 
 
  1. class MainActivity : AppCompatActivity() {    val task_list = ArrayList<String>()         //任务清单表 
  2.     override fun onCreate(savedInstanceState: Bundle?) { 
  3.        super.onCreate(savedInstanceState) 
  4.        savedInstanceState?.let {           val arrayList = savedInstanceState.get("ToDoList"
  5.            task_list.addAll(arrayList as List<String>) 
  6.        }       var adapter=TodoAdapter(task_list)      //定义适配器 
  7.        var ui = MainUI(adapter)                //定义将要使用的Anko UI 布局 
  8.        ui.setContentView(this)                 //给Activity设置Anko布局 
  9.    }   override fun onSaveInstanceState(outState: Bundle?) { 
  10.        outState?.putStringArrayList("ToDoList", task_list) 
  11.        super.onSaveInstanceState(outState) 
  12.      } 
  13.  }  

task_list是ArrayList,将填充ListView的TodoAdapter。MainUI(adapter)是我们的Anko UI文件,它采用TodoAdapter类作为适配器参数。所以,接下来我们再创建一个TodoAdapter类。

用于ListView的TodoAdapter适配器

TodoAdapter类有一个ArrayList<String>类型的list,并且继承了BaseAdapter。所以我们需要重写一下四个方法:

public int getCount()public Object getItem(int i)public long getItemId(int i)public View getView(int i, View view, ViewGroup viewGroup)

在getView()方法中我们需要使用Anko设计一个表元素的布局。

 
 
  1. public int getCount()public Object getItem(int i)public long getItemId(int i)public View getView(int i, View view, ViewGroup viewGroup) 
  2.  
  3.         在getView()方法中我们需要使用Anko设计一个表元素的布局。 
  4.  
  5. override fun getView(i : Int, v : View?, parent : ViewGroup?) : View { 
  6.               return with(parent!!.context) {              //任务数从1开始 
  7.               var taskNum: Int = i +1 
  8.              //清单表元素布局 
  9.              linearLayout { 
  10.                    lparams(width = matchParent, height = wrapContent) 
  11.                    padding = dip(10) 
  12.                    orientation = HORIZONTAL                   //任务号 
  13.                   textView { 
  14.                        id = R.id.taskNum 
  15.                        text=""+taskNum 
  16.                        textSize = 16f 
  17.                        typeface = Typeface.MONOSPACE 
  18.                        padding =dip(5) 
  19.                   }                  //任务名 
  20.                   textView { 
  21.                         id = R.id.taskName 
  22.                        text=list.get(i) 
  23.                        textSize = 16f 
  24.                        typeface = DEFAULT_BOLD 
  25.                        padding =dip(5) 
  26.                   } 
  27.              } 
  28.          } 
  29.       } 
  • 在这个方法中,我们返回一个包含一个horizontalListView布局列表项的视图。这是使用Kotlin的with语法完成的,它允许我们一次在对象实例上调用很多方法。
  • 每个列表项包含两个textview用于显示任务号和任务名称。
  • linearLayout,textView是扩展功能。扩展功能使我们有能力启用具有新功能的任何类。
  • text,textSize,typeface在android.widget.TextView有getter和setter方法,padding是Anko添加的属性。

继续下一步,我们需要定义列表的操作功能。因此,我们需要在TodoAdapter中定义add(String)和delete(Int)方法。add(String)将任务名称作为参数添加到任务中。delete(Int)将任务所在的位置作为参数来删除任务。下面是具体的实现:

 
 
  1. //将任务添加到任务清单的方法      
  2.  fun add(text: String) {          
  3.        list.add(list.size, text)          
  4.        notifyDataSetChanged()          //更新数据    }       //将任务从任务清单中移除的方法      
  5.  fun delete(i:Int) {         
  6.        list.removeAt(i)          
  7.        notifyDataSetChanged()          //更新数据      
  8.  }  

所以,现在我们设计了列表,我们也可以添加和删除项目到我们的列表中。接下来完成此适配器类的代码。

 
 
  1.  TodoAdapter(val list: ArrayList<String> = ArrayList<String>()) : BaseAdapter() {     
  2.                override fun getView(i : Int, v : View?, parent : ViewGroup?) : View {         
  3.                          return with(parent!!.context) {             
  4.                                    //taskNum will serve as the S.Noof the list starting from 1             
  5.                                    var taskNum: Int = i +1            
  6.                                    //Layout for a list view item             
  7.                                    linearLayout {                 
  8.                                           id = R.id.listItemContainer                 
  9.                                           lparams(width = matchParent, height = wrapContent)                 
  10.                                           padding = dip(10)                 
  11.                                           orientation = HORIZONTAL                 
  12.                                              textView {                     
  13.                                                    id = R.id.taskNum                    
  14.                                                    text=""+taskNum                    
  15.                                                    textSize = 16f                   
  16.                                                    typeface = Typeface.MONOSPACE                 
  17.                                                    padding =dip(5)                
  18.                                              }                 
  19.                                           textView {                    
  20.                                                   id = R.id.taskName                    
  21.                                                   text=list.get(i)                    
  22.                                                   textSize = 16f                     
  23.                                                   typeface = DEFAULT_BOLD                    
  24.                                                   padding =dip(5)                
  25.                                            }            
  26.                                     }         
  27.                                 }    
  28.                            }    
  29.            override fun getItem(position : Int) : String {         
  30.                       return list[position     
  31.             }     
  32.            override fun getCount() : Int {        
  33.                       return list.size    
  34.             }     
  35.            override fun getItemId(position : Int) : Long {         
  36.                      //can be used to return the item's ID column of table         
  37.                      eturn 0L     
  38.             }     
  39.            //function to add an item to the list     
  40.            fun add(text: String) {              
  41.                    list.add(list.size, text)         
  42.                    notifyDataSetChanged()    
  43.            }     
  44.           //function to delete an item from list    
  45.           fun delete(i:Int) {         
  46.                    list.removeAt(i)         
  47.                    notifyDataSetChanged()    
  48.           } 
  49.  

注意,使用Anko DSL类中必须要导入org.jetbrains.anko.*。

设计项目的外观

Anko为我们提供了在单独的Kotlin类中为Activity使用UI的便利。因此,每个屏幕都可以被认为是Kotlin类的UI-Activity匹配对。这个UI类是通过继承在org.jetbrains.anko包中定义的AnkoComponent<T>接口的功能来实现的。

除了这个接口,JetBrains还提供免费的DSL布局预览功能。下面是Anko DSL布局预览在Android Studio中的样子:

Anko Preview的相应插件可以从这里下载。请注意,在撰写本文时,Android Studio 2.2的Anko DSL 布局预览被列为开源issue。

回到正题,我们接下来设计MainUI类展示所有任务列表。MainUI类继承了AnkoComponent<T>接口,其中T指的是UI的所有者,activity的内容将会是这个UI。在我们的例子中,所有者就是我们已经在上面定义的MainActivity。接下来,在初始化时,我们必须将TodAadapter对象传递给此类,因为此适配器将用于填充列表。所以,MainUI声明变成:

 
 
  1. class MainUI(val todoAdapter : TodoAdapter) : AnkoComponent<MainActivity> 

现在我们需要重写方法 createView() ,使用 AnkoContext 对象作为参数并返回一个View 类型:

 
 
  1. override fun createView(ui: AnkoContext<MainActivity>): View = with(ui) { 
  2.  
  3. } 

我们在createView() 方法中UI定义返回给所有者即activity,在这里也就是MainActivity,所以接下来写createView() 方法:

Step1-设计首页

最初,首页是空列表。所以,我们有一个textView要求用户创建一天的Todo List:

 
 
  1. return relativeLayout {         
  2.                          //声明ListView         
  3.                         var todoList : ListView? =null        
  4.                         //当没有任务时显示textView内容"What's your Todo List for today?"         
  5.                         val hintListView = textView("What's your Todo List for today?") {             
  6.                                             textSize = 20f       
  7.                           }.lparams {             
  8.                     centerInParent()         
  9.                    } 
  10.        }  

centerInParent() 是将视图的布局定义为垂直和水平相对中心的辅助方法。因为它是一个todo性质的应用,其本质在于显示任务的列表。所以,我们在这里定义listView:

 
 
  1. //listView   
  2.   verticalLayout {                 
  3.                todoList=listView {                 
  4.               //assign adapter                 
  5.              adapter = todoAdapter               
  6.               }            
  7.       }.lparams {                 
  8.           margin = dip(5)            

todoAdapter是我们在MainUI类声明中定义成员变量。我们用todoAdapter的值初始化listView的adapter,这是一个TodoAdpater类的对象,将会用于填充列表。

为了帮助用户添加任务,我们在主屏幕的右下方提供了一个Material design风格的floatingActionButton。所以我们使用Anko编程floatingActionButton为:

 
 
  1. floatingActionButton {                           
  2.          imageResource = android.R.drawable.ic_input_add         
  3.           }.lparams {             
  4.                           //设置按钮在屏幕的右下方             
  5.                            margin = dip(10)             
  6.                            alignParentBottom()             
  7.                            alignParentEnd()             
  8.                            alignParentRight()             
  9.                            gravity = Gravity.BOTTOM or Gravity.END        
  10.     }  

篇幅所限,Step2、Step3请点击左下角“阅读原文”查看全部文章。

最后的想法

我们在开发此to-do app时没有使用任何XML布局资源,但我们能够以类似的风格设计应用程序。Anko从应用程序的activity或fragments中消除了呈现数据的负担,响应用户交互,连接数据库等这些负担。另外,隔离UI和Activity类使得应用程序更接近MVP(Model-View-Presenter)架构。你可以从这里了解Anko的高级功能。

虽然它有一些缺点,如较慢的编译和应用程序体积大,因为它在复用,维护和测试代码方面打包了很多东西。

第一次翻译这么长的文章,纯手打,如果有什么错误的地方还请指出包涵!也是因为自己对Kotlin的兴趣所以找了这篇文章来翻译,有兴趣的朋友可以看看学习一下。





作者:Ankul Jain
来源:51CTO
目录
相关文章
|
24天前
|
安全 Java Android开发
安卓开发中的新趋势:Kotlin与Jetpack的完美结合
【6月更文挑战第20天】在不断进化的移动应用开发领域,Android平台以其开放性和灵活性赢得了全球开发者的青睐。然而,随着技术的迭代,传统Java语言在Android开发中逐渐显露出局限性。Kotlin,一种现代的静态类型编程语言,以其简洁、安全和高效的特性成为了Android开发中的新宠。同时,Jetpack作为一套支持库、工具和指南,旨在帮助开发者更快地打造优秀的Android应用。本文将探讨Kotlin与Jetpack如何共同推动Android开发进入一个新的时代,以及这对开发者意味着什么。
|
2天前
|
存储 移动开发 Android开发
使用kotlin Jetpack Compose框架开发安卓app, webview中h5如何访问手机存储上传文件
在Kotlin和Jetpack Compose中,集成WebView以支持HTML5页面访问手机存储及上传音频文件涉及关键步骤:1) 添加`READ_EXTERNAL_STORAGE`和`WRITE_EXTERNAL_STORAGE`权限,考虑Android 11的分区存储;2) 配置WebView允许JavaScript和文件访问,启用`javaScriptEnabled`、`allowFileAccess`等设置;3) HTML5页面使用`<input type="file">`让用户选择文件,利用File API;
|
7天前
|
安全 Java Android开发
探索安卓应用开发中的Kotlin语言优势
【7月更文挑战第8天】 在安卓开发的广阔天地中,Kotlin以其优雅的语法、现代化的特性和高效的性能成为了开发者的新宠。本文将深入探讨Kotlin在安卓应用开发中所展现的独特魅力,从语言特性到实际应用案例,揭示其如何简化代码编写、提升开发效率,并增强应用性能。通过对比分析,我们将一同见证Kotlin如何在众多编程语言中脱颖而出,成为安卓开发领域的一股清新之风。
192 11
|
3天前
|
Android开发 Kotlin
Android面试题之kotlin中怎么限制一个函数参数的取值范围和取值类型等
在Kotlin中,限制函数参数可通过类型系统、泛型、条件检查、数据类、密封类和注解实现。例如,使用枚举限制参数为特定值,泛型约束确保参数为Number子类,条件检查如`require`确保参数在特定范围内,数据类封装可添加验证,密封类限制为一组预定义值,注解结合第三方库如Bean Validation进行校验。
16 6
|
4天前
|
Android开发 Kotlin
Android面试题之 Kotlin中退出迭代器的方式有哪些
在Android和Kotlin中,遍历集合时可使用迭代器结合`break`提前终止循环。例如,使用`while`和迭代器,或用`forEach`配合`return@forEach`来中断遍历。若需退出外层函数,可定义自定义标签。在遍历并删除元素时,这些技巧尤其有用。
13 3
|
7天前
|
Java Android开发 开发者
探索安卓应用开发的未来:Kotlin语言的崛起与挑战
【7月更文挑战第8天】随着移动设备在人们日常生活中扮演着越来越重要的角色,安卓应用开发的技术趋势和编程语言选择对开发者来说变得至关重要。本文将深入探讨Kotlin语言如何成为安卓开发的首选,分析其带来的优势及面临的挑战,并预测其未来的发展方向。
|
5天前
|
开发者 Kotlin Android开发
Kotlin协程在Android开发中的应用
【7月更文挑战第10天】Kotlin协程简化了Android异步编程,提供轻量级并发。挂起函数让异步代码看起来同步,不阻塞线程,便于管理。在项目中,添加Kotlin和协程依赖,如`kotlinx.coroutines-core`和`kotlinx-coroutines-android`。使用`CoroutineScope`和`launch`处理耗时任务,如网络请求,避免主线程阻塞。挂起函数和调度器控制执行上下文,适应不同任务需求。
|
27天前
|
安全 Java 编译器
Android面试题之Java 泛型和Kotlin泛型
**Java泛型是JDK5引入的特性,用于编译时类型检查和安全。泛型擦除会在运行时移除类型参数,用Object或边界类型替换。这导致几个限制:不能直接创建泛型实例,不能使用instanceof,泛型数组与协变冲突,以及在静态上下文中的限制。通配符如<?>用于增强灵活性,<? extends T>只读,<? super T>只写。面试题涉及泛型原理和擦除机制。
23 3
Android面试题之Java 泛型和Kotlin泛型
|
17天前
|
安全 Android开发 Kotlin
Android面试题之Kotlin协程并发问题和互斥锁
Kotlin的协程提供轻量级并发解决方案,如`kotlinx.coroutines`库。`Mutex`用于同步,确保单个协程访问共享资源。示例展示了`withLock()`、`lock()`、`unlock()`和`tryLock()`的用法,这些方法帮助在协程中实现线程安全,防止数据竞争。
20 1
|
1天前
|
Android开发 Kotlin
kotlin开发安卓app,如何让布局自适应系统传统导航和全面屏导航
使用`navigationBarsPadding()`修饰符实现界面自适应,自动处理底部导航栏的内边距,再加上`.padding(bottom = 10.dp)`设定内容与屏幕底部的距离,以完成全面的布局适配。示例代码采用Kotlin。
31 15