前言
凌晨的谷歌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实现如下:
- verticalLayout{
- imageView(R.drawable.anko_logo).
- lparams(width= matchParent) {
- padding = dip(20)
- margin = dip(15)
- }
- button("Tap to Like") {
- onClick { toast("Thanks for the love!") }
- }
- }
我们定义了一个垂直的线性布局作为容器包含图片和按钮,使用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中,这样我们就可以顺利编译项目了。
- compile [size=1em]'org.jetbrains.anko:anko-sdk15:0.8.3'
- // sdk19,21,23 也可以使用
可以根据你项目的minSdkVersion来添加这个依赖,上面的例子说明15<=minSdkVersion<19,你可以在Anko的GitHub库中找到自己需要的其他Anko依赖库。
我们准备使用下面的依赖库:
- compile 'org.jetbrains.anko:anko-design:0.8.3'
- compile 'org.jetbrains.anko:anko-appcompat-v7:0.8.3'
在Activity中调用Anko布局:
我们不再使用XML来写布局文件,所以我们不需要XML View,所以也不需要findViewById()方法了。这里我们假设我们的Anko布局类为MainUI,然后我们可以开始写我们的activit内容:
- var ui =MainUI() //MainUI类代替了XML布局
- ui.setContentView(this) //this代表Activity类
现在我们创建一个Kotlin文件MainActivity.kt,写上如下代码:
- class MainActivity : AppCompatActivity() { val task_list = ArrayList<String>() //任务清单表
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- savedInstanceState?.let { val arrayList = savedInstanceState.get("ToDoList")
- task_list.addAll(arrayList as List<String>)
- } var adapter=TodoAdapter(task_list) //定义适配器
- var ui = MainUI(adapter) //定义将要使用的Anko UI 布局
- ui.setContentView(this) //给Activity设置Anko布局
- } override fun onSaveInstanceState(outState: Bundle?) {
- outState?.putStringArrayList("ToDoList", task_list)
- super.onSaveInstanceState(outState)
- }
- }
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设计一个表元素的布局。
- public int getCount()public Object getItem(int i)public long getItemId(int i)public View getView(int i, View view, ViewGroup viewGroup)
- 在getView()方法中我们需要使用Anko设计一个表元素的布局。
- override fun getView(i : Int, v : View?, parent : ViewGroup?) : View {
- return with(parent!!.context) { //任务数从1开始
- var taskNum: Int = i +1
- //清单表元素布局
- linearLayout {
- lparams(width = matchParent, height = wrapContent)
- padding = dip(10)
- orientation = HORIZONTAL //任务号
- textView {
- id = R.id.taskNum
- text=""+taskNum
- textSize = 16f
- typeface = Typeface.MONOSPACE
- padding =dip(5)
- } //任务名
- textView {
- id = R.id.taskName
- text=list.get(i)
- textSize = 16f
- typeface = DEFAULT_BOLD
- padding =dip(5)
- }
- }
- }
- }
- 在这个方法中,我们返回一个包含一个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)将任务所在的位置作为参数来删除任务。下面是具体的实现:
- //将任务添加到任务清单的方法
- fun add(text: String) {
- list.add(list.size, text)
- notifyDataSetChanged() //更新数据 } //将任务从任务清单中移除的方法
- fun delete(i:Int) {
- list.removeAt(i)
- notifyDataSetChanged() //更新数据
- }
所以,现在我们设计了列表,我们也可以添加和删除项目到我们的列表中。接下来完成此适配器类的代码。
- TodoAdapter(val list: ArrayList<String> = ArrayList<String>()) : BaseAdapter() {
- override fun getView(i : Int, v : View?, parent : ViewGroup?) : View {
- return with(parent!!.context) {
- //taskNum will serve as the S.No. of the list starting from 1
- var taskNum: Int = i +1
- //Layout for a list view item
- linearLayout {
- id = R.id.listItemContainer
- lparams(width = matchParent, height = wrapContent)
- padding = dip(10)
- orientation = HORIZONTAL
- textView {
- id = R.id.taskNum
- text=""+taskNum
- textSize = 16f
- typeface = Typeface.MONOSPACE
- padding =dip(5)
- }
- textView {
- id = R.id.taskName
- text=list.get(i)
- textSize = 16f
- typeface = DEFAULT_BOLD
- padding =dip(5)
- }
- }
- }
- }
- override fun getItem(position : Int) : String {
- return list[position
- }
- override fun getCount() : Int {
- return list.size
- }
- override fun getItemId(position : Int) : Long {
- //can be used to return the item's ID column of table
- eturn 0L
- }
- //function to add an item to the list
- fun add(text: String) {
- list.add(list.size, text)
- notifyDataSetChanged()
- }
- //function to delete an item from list
- fun delete(i:Int) {
- list.removeAt(i)
- notifyDataSetChanged()
- }
- }
注意,使用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声明变成:
- class MainUI(val todoAdapter : TodoAdapter) : AnkoComponent<MainActivity>
现在我们需要重写方法 createView() ,使用 AnkoContext 对象作为参数并返回一个View 类型:
- override fun createView(ui: AnkoContext<MainActivity>): View = with(ui) {
- }
我们在createView() 方法中UI定义返回给所有者即activity,在这里也就是MainActivity,所以接下来写createView() 方法:
Step1-设计首页
最初,首页是空列表。所以,我们有一个textView要求用户创建一天的Todo List:
- return relativeLayout {
- //声明ListView
- var todoList : ListView? =null
- //当没有任务时显示textView内容"What's your Todo List for today?"
- val hintListView = textView("What's your Todo List for today?") {
- textSize = 20f
- }.lparams {
- centerInParent()
- }
- }
centerInParent() 是将视图的布局定义为垂直和水平相对中心的辅助方法。因为它是一个todo性质的应用,其本质在于显示任务的列表。所以,我们在这里定义listView:
- //listView
- verticalLayout {
- todoList=listView {
- //assign adapter
- adapter = todoAdapter
- }
- }.lparams {
- margin = dip(5)
- }
todoAdapter是我们在MainUI类声明中定义成员变量。我们用todoAdapter的值初始化listView的adapter,这是一个TodoAdpater类的对象,将会用于填充列表。
为了帮助用户添加任务,我们在主屏幕的右下方提供了一个Material design风格的floatingActionButton。所以我们使用Anko编程floatingActionButton为:
- floatingActionButton {
- imageResource = android.R.drawable.ic_input_add
- }.lparams {
- //设置按钮在屏幕的右下方
- margin = dip(10)
- alignParentBottom()
- alignParentEnd()
- alignParentRight()
- gravity = Gravity.BOTTOM or Gravity.END
- }
篇幅所限,Step2、Step3请点击左下角“阅读原文”查看全部文章。
***的想法
我们在开发此to-do app时没有使用任何XML布局资源,但我们能够以类似的风格设计应用程序。Anko从应用程序的activity或fragments中消除了呈现数据的负担,响应用户交互,连接数据库等这些负担。另外,隔离UI和Activity类使得应用程序更接近MVP(Model-View-Presenter)架构。你可以从这里了解Anko的高级功能。
虽然它有一些缺点,如较慢的编译和应用程序体积大,因为它在复用,维护和测试代码方面打包了很多东西。
***次翻译这么长的文章,纯手打,如果有什么错误的地方还请指出包涵!也是因为自己对Kotlin的兴趣所以找了这篇文章来翻译,有兴趣的朋友可以看看学习一下。