Anko for Android

Anko 是一个使开发Android应用更简单更快捷的库,Anko使你的代码简洁易懂, 使开发者不用再在意Android SDK对Java版本的限制(目前还不支持Java8 =。= ).

Anko版本的 hello world :

verticalLayout {
    val name = editText()
    button("Say Hello") {
        onClick { toast("Hello, ${name.text}!") }
    }
}

上面的代码创建了一个Button,放在 LinearLayout 内, 并为其设置了一个点击监听器OnClickListener .

上面是一个DSL(Domain Specific Language),使用的是 Kotlin语言.

DSL,即 Domain Specific Language,领域相关语言。什么是 DSL,说白了它就是某个行业中的行话。

[TOC]

Why Anko?

为啥 DSL?

平时开发android, UI写在xml中,这就导致了下面的几个问题 :

  • It is not typesafe
  • It is not null-safe
  • It forces you to write almost the same code for every layout you make
  • XML is parsed on the device wasting CPU time and battery 渲染xml为对象过程耗时耗电
  • Most of all, it allows no code reuse. 大部分不能重用

但是全部只在代码中写UI,这很难,不仅代码丑,而且冗余难维护,下面是 Ktolin版本的(Java甚至更长):

val act = this
val layout = LinearLayout(act)
layout.setOrientation(LinearLayout.VERTICAL)
val name = EditText(act)
val button = Button(act)
button.setText("Say Hello")
button.setOnClickListener {
    Toast.makeText(act, "Hello, ${name.getText()}!", Toast.LENGTH_SHORT).show()  
}
layout.addView(name)
layout.addView(button)

DSL 就不一样类,相同的逻辑,但简洁易懂, 易于编写而且没有运行开销(runtime overhead)看下面的代码:

verticalLayout {
    val name = editText()
    button("Say Hello") {
        onClick { toast("Hello, ${name.text}!") }
    }
}

为啥不用 Scaloid?

Scaloid 是一个类似与 Scala 的库, 有很多非常酷的特性可供 Scala 开发者使用. Anko主要是针对 Java 和 Kotlin developers.

兼容已有的代码

不需要用Anko重写所有的UI, 你可以保留原有的Java代码. 此外, 如果你想写一个 Kotlin的activity类并且由于某些需求需要使用 inflate来渲染xml, 你完全可以按照原来的写法:

// Same as findViewById(), simpler to use
val name = find<TextView>(R.id.name)
name.hint = "Enter your name"
name.onClick { /*do something*/ }

工作原理

There is no :tophat:. Anko 由一些 Kotlin扩展函数和属性,被设置成类型安全(type-safe builders)的, under Type Safe Builders.

他们繁琐的手工编写所有这些扩展, 使用Android SDK的源码中的 android.jar 文件自动生成

可扩展吗?

答案是: yes.
例如. 你可能想使用 MapView 在DSL中.你可以编写下面的代码(kotlin文件中),然后就可已到处使用了

public inline fun ViewManager.mapView() = mapView {}
public inline fun ViewManager.mapView(init: MapView.() -> Unit): MapView {
    return ankoView({ MapView(it) }, init)
}

{ MapView(it) } 是你自定义View的一个工厂方法View. 接受一个 Context .

frameLayout {
    val mapView = mapView().lparams(width = matchParent)
}

如果你想创建一个 顶级的 DSL,看这里Extending Anko.

使用 Gradle

这里有个例子 template project 展示类如果在Android中Gradle配置.

基本上,你只需要配置 repository 和一个 compile dependency:

dependencies {
    compile 'org.jetbrains.anko:anko-sdk15:0.7.1' // sdk19, sdk21, sdk23 are also available
    compile 'org.jetbrains.anko:anko-support-v4:0.7.1' // In case you need support.v4 bindings
}

当作 Jar library使用

加入你的项目不是基于Gradl, 不需要配置 Maven. 只要添加这里 的jar包即可.

编译 Anko

如何编译看under Building.

理解 Anko

Anko 是使用 Kotlin语言编写的.
如果不熟悉Kotlinkotlinlang.org.
Kotlin与Java很类似,所以很容易学.

基础

Anko中, 你不需要继承其他奇怪的类,只要标准的Activity, Fragment, FragmentActivity 或者其他任意的类

首先, 在使用Anko的DSL的类中导入 org.jetbrains.anko.* .

DSL 可以在 onCreate()中使用:

override fun onCreate(savedInstanceState: Bundle?) {
    super<Activity>.onCreate(savedInstanceState)
    
    verticalLayout {
        padding = dip(30)
        editText {
            hint = "Name"
            textSize = 24f
        }
        editText {
            hint = "Password"
            textSize = 24f
        }
        button("Login") {
            textSize = 26f
        }
    }
}

不需要显示的调用 setContentView(R.layout.something), Anko 自动为Activity(只会对Activity)进行 set content view

padding, hinttextSize扩展属性. 大多数 View 具有这些属性,允许使用text = "Some text" 代替 setText("Some text").

verticalLayout (一个竖直方向的 LinearLayout), editTextbutton are
扩展函数. 这些函数存在与ANdroid 框架中的大部View中, Activities, Fragments ( android.support 包中的) 甚至 Context同样适用.

如果有一个 Context 实例, 可以写出下面的DSL结构:

val name = with(myContext) {
    editText {
        hint = "Name"
    }
}

变量 name 成为了 EditText类型.

Helper 方法

你可能注意到了,前面章节中 button 方法接了一个字符串参数,这样的Helper方法同样使用与 TextView, EditText, ButtonImageView.

如果你不需要 View 其他的属性,你可以省略 {} 直接写 button("Ok") 或只有 button():

verticalLayout {
    button("Ok")
    button("Cancel")
}

Layouts 和 LayoutParams

在父布局中布局控件可能需要使用 LayoutParams. xml中长这样:

<ImageView 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android_layout_marginLeft="5dip"
    android_layout_marginTop="10dip"
    android:src="@drawable/something" />

Anko中, 在View的后面使用 lparams来实现类似与xml的 LayoutParams

linearLayout {
    button("Login") {
        textSize = 26f
    }.lparams(width = wrapContent) {
        horizontalMargin = dip(5)
        topMargin = dip(10)
    }
}

如果指定了 lparams,但是没有指定 width 或者 height, 默认是 WRAP_CONTENT.但是你可以自己通过使用named arguments指定.

注意下面一些方便的属性:

  • horizontalMargin 同时设置 left 和 right margins,
  • verticalMargin 同时设置 top 和 bottom
  • margin 同时设置4个方向的 margins.

注意 lparams 的使用在不同的布局中是不同的, 例如在 RelativeLayout中:

val ID_OK = 1

relativeLayout {
    button("Ok") {
        id = ID_OK
    }.lparams { alignParentTop() }
  
    button("Cancel").lparams { below(ID_OK) }
}

Listeners

设置listeners:

button("Login") {
    onClick {
        login(name, password)
    }
}

下面的效果一样:

button.setOnClickListener(object : OnClickListener {
    override fun onClick(v: View) {
        login(name, password)
    }
})

当一个Listener有多个方法时,Anko就显得很方便类. 看下面的代码(没有使用Anko):

seekBar.setOnSeekBarChangeListener(object: OnSeekBarChangeListener {
    override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
        // Something
    }
    override fun onStartTrackingTouch(seekBar: SeekBar?) {
        // Just an empty method
    }
    override fun onStopTrackingTouch(seekBar: SeekBar) {
        // Another empty method
    }
})

使用了Anko:

seekBar {
    onSeekBarChangeListener {
        onProgressChanged { seekBar, progress, fromUser ->
            // Something
        }
    }
}

如果你同时设置了onProgressChangedonStartTrackingTouch , 两个方法将被合并. 对于多个相同的方法,最后的一个有效.

Resources, Colors 和 Dimensions

Using resource identifiers

前面的所有例子直接使用的 Java的字符串,但是大多数时候字符串都是放在 res/values/ 目录下,并且是运行时调用的,例如 getString(R.string.login).

幸运的是,Anko中可以使用以下两个 helper方法 (button(R.string.login)) 和 (button { textResource = R.string.login }).

注意,这些属性不是 text, hint, image, 而是 textResource, hintResource and imageResource.

Resource properties always throw AnkoException when read.

Colors

两个简单的扩展函数使代码更加易懂。

Function Result
0xff0000.opaque <span style="color:#ff0000">non-transparent red</span>
0x99.gray.opaque <span style="color:#999">non-transparent #999999 gray</span>

Dimensions

你可以指定 dimension 的 dip (density-independent pixels) 或 sp (scale-independent pixels)值: dip(dipValue)sp(spValue). 注意 textSize属性默认接受sp (textSize = 16f). 使用 px2dippx2sp 相互转换.

Instance shorthands

在Activity中,有时你需要传一个 Context实例给一个 Android SDK中的方法,通常你会写 this, 如果在内部类呢?你可能写SomeActivity.this ,如果你使用 Kotlin,你只需写 this@SomeActivity
使用 Anko,你可以只写 ctx. ctxActivityService 或者 Fragment (使用的 getActivity() )内部的一个属性. 你也可以使用act扩展属性获取 Activity实例.

UI wrapper

开始使用Anko 之前,将 UI tag 作为 DSL 顶级元素:

UI {
    editText {
        hint = "Name"
    }
}

这将更易于扩展 DSL ,因为你必须声明一个函数 ViewManager.customView.
看这里 Extending Anko 获取更多信息.

Include tag

使用 include tag 很容易向 DSL 插入 一个 XML layout :

include<View>(R.layout.something) {
    backgroundColor = Color.RED
}.lparams(width = matchParent) { margin = dip(12) }

通常可以使用 lparams , 如果类型不是 View,仍然可以用 {}:

include<TextView>(R.layout.textfield) {
    text = "Hello, world!"
}

Styles

Anko 支持 styling: style 是一个简单的函数,接受一个View, 效果作用于这个 View , 并且当这个View 是一个ViewGroup 时,可以可以递归的作用与 这个View的 child View:

verticalLayout {
    editText {
        hint = "Name"
    }
    editText {
        hint = "Password"
    }
}.style { view -> when(view) {
    is EditText -> view.textSize = 20f
}}


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

推荐阅读更多精彩内容