专业编程基础技术教程

网站首页 > 基础教程 正文

Android框架Anko:Anko布局有什么好

ccvgpt 2024-07-24 11:08:14 基础教程 11 ℃

为什么选择Anko布局?

Android框架Anko:Anko布局有什么好

为何使用 DSL?

默认情况下,Android的UI使用XML来写的。这种方式有以下几个不方便的地方:

  • 不是类型安全的;

  • 不是空指针安全的;

  • 它着力于让你的每一个布局都写几乎一样的代码;

  • XML在设备上进行解析会浪费CPU时间和电量;

  • 最重要的是,它不允许代码重用。

当你使用程序代码来创建UI时,这通常让你感到为难,代码丑陋还不易于维护。 这是一个原生的Kotlin版本的代码 (在Java中代码可能更多):

val act = this

val layout = LinearLayout(act)

layout.orientation = LinearLayout.VERTICAL

val name = EditText(act)

val button = Button(act)

button.text = "Say Hello"

button.setOnClickListener {

Toast.makeText(act, "Hello, ${name.text}!", Toast.LENGTH_SHORT).show()

}

layout.addView(name)

layout.addView(button)

使用 DSL 使得处理同样的逻辑时,代码变得易读、易写并且还没有运行时额外的性能损耗。看下面这端代码:

verticalLayout {

val name = editText()

button("Say Hello") {

onClick { toast("Hello, ${name.text}!") }

}

}

注意: onClick() 支持协同程序 (挂起lambda表达式) 所以你可以直接在里面写你的异步代码,而不用显示的 async(UI) 调用。

支持已有代码

你不需要用Anko重写所有的UI。你可保持过去用Java写的类。此外,你因为某些原因仍然想写一个Kotlin 的Activity类并填充XML布局,你可以使用视图属性,让事情变的更简单:

// Same as findViewById() but simpler to use

val name = find<TextView>(R.id.name)

name.hint = "Enter your name"

name.onClick { /*do something*/ }

你可以使用 Kotlin Android扩展让代码变的更加简洁。

它是怎么工作的

没有 . Anko 由Kotlin中的扩展函数和属性到类型安全的建造器(在under Type Safe Builders中用详细描述)等特性组成。

完全手写这些扩展的确有点无聊,你可以使用Android SDK中的 android.jar 自动生成。

它可以扩展吗?

简单的回答: 可以。

例如,已可能想在DSL中使用一个 MapView。 只需要在任何一个你可以导入的Kotlin文件中这样写:

inline fun ViewManager.mapView() = mapView(theme = 0) {}

inline fun ViewManager.mapView(init: MapView.() -> Unit): MapView {

return ankoView({ MapView(it) }, theme = 0, init = init)

}

{ MapView(it) } 是一个关于自定义 View 的工厂函数。 它接收一个 Context 实例。

所以,现在你可以这样写:

frameLayout {

val mapView = mapView().lparams(width = matchParent)

}

如果你想你的使用者可以应用自定义主题,也可以这样写:

inline fun ViewManager.mapView(theme: Int = 0) = mapView(theme) {}

inline fun ViewManager.mapView(theme: Int = 0, init: MapView.() -> Unit): MapView {

return ankoView({ MapView(it) }, theme, init)

}

在你的项目中使用Anko布局

包含这些依赖库:

dependencies {

// Anko Layouts

compile "org.jetbrains.anko:anko-sdk25:$anko_version" // sdk15, sdk19, sdk21, sdk23 are also available

compile "org.jetbrains.anko:anko-appcompat-v7:$anko_version"

// Coroutine listeners for Anko Layouts

compile "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"

compile "org.jetbrains.anko:anko-appcompat-v7-coroutines:$anko_version"

}

请阅读 基于Gradle的工程 章节,以获取详细的信息。

理解Anko

基础

在Anko中,你不需要继承任何特别的类,仅仅使用标准的 Activity, Fragment, FragmentActivity 或是其他你想用的/

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

DSL 允许在 onCreate() 中使用:

override fun onCreate(savedInstanceState: Bundle?) {

super.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会自动为 Activities 设置上下文视图(也只能是它们)。

hint 和 textSize 是 合成扩展属性 绑定了 JavaBean风格的 getters 和 setters, padding 是一个Anko的 扩展属性。 几乎所有的 View属性都允许你这样写,像是 text = "Some text" 而不是 setText("Some text").

verticalLayout (一个已经将方向设置为 LinearLayout.VERTICAL 的 LinearLayout), editText 和 button 是用来构建一个新的视图实例并将它们加入父视图中的扩展函数 。我们像Blocks一样引用这些函数。

Blocks几乎在Android框架中的每一个 View 中都存在,它们工作在 Activities, Fragments (在 android.support 中的也行) 甚至是 Context 中。 例如, 你想要一个 AnkoContext 实例。 你可以像这样写 blocks :

val name: EditText = with(ankoContext) {

editText {

hint = "Name"

}

}

Anko组件

尽管你可以直接使用DSL (在 onCreate() 或任何你想使用的地方),而不需要创建任何多余的类。往往将UI分布在不同的类中会方便些。如果你使用框架提供的 AnkoComponent 接口,你可以免费得到 DSL 布局预览的特性 。

class MyActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {

super.onCreate(savedInstanceState, persistentState)

MyActivityUI().setContentView(this)

}

}

class MyActivityUI : AnkoComponent<MyActivity> {

override fun createView(ui: AnkoContext<MyActivity>) = with(ui) {

verticalLayout {

val name = editText()

button("Say Hello") {

onClick { ctx.toast("Hello, ${name.text}!") }

}

}

}

}

blocks 助手

你可能之前就已经注意到了, button() 函数再之前的章节中接受了一个 String 类型的参数。 blocks助手在频繁使用的视图中都有存在,例如: TextView、 EditText、Button 或是 ImageView。

如果你不需要为某个特别的 View 设置任何属性, 你可以隐藏 {},直接写 button("Ok") 甚至只写 button():

verticalLayout {

button("Ok")

button(R.string.cancel)

}

主题化的 blocks

Anko 提供“可制定主题” 的 blocks 版本,包括 blocks 助手:

verticalLayout {

themedButton("Ok", theme = R.style.myTheme)

}

布局和布局参数

组件在父容器中的位置可以使用 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中, 你可以在 视图 描述的后面使用 lparams() 来设定 LayoutParams:

linearLayout {

button("Login") {

textSize = 26f

}.lparams(width = wrapContent) {

horizontalMargin = dip(5)

topMargin = dip(10)

}

}

如果你使用了 lparams(),却隐藏了 width 或/和 height,它们默认被设置成 wrapContent。但是你总可以显示的传递它们:使用命名参数.

这用一些方便的属性助手值得注意:

  • horizontalMargin 设置左侧和右侧的外边距,

  • verticalMargin 设置顶部和底部的

  • margin 同时设置所有的外边边距。

注意对于不同的布局 lparams() 也是不同的,例如,在使用 RelativeLayout 的情况下:

val ID_OK = 1

relativeLayout {

button("Ok") {

id = ID_OK

}.lparams { alignParentTop() }

button("Cancel").lparams { below(ID_OK) }

}

监听器

Anko含有监听器助手,可以无缝的支持协程。你可以直接的在你的监听器中编写异步代码!

button("Login") {

onClick {

val user = myRetrofitService.getUser().await()

showUser(user)

}

}

这样写也是一样的:

button.setOnClickListener(object : OnClickListener {

override fun onClick(v: View) {

launch(UI) {

val user = myRetrofitService.getUser().await()

showUser(user)

}

}

})

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

}

}

}

如果你在同一个 视图 中使用了 onProgressChanged() 和 onStartTrackingTouch() ,这两个 "部分定义" 的监听器将会被合并。同样的监听器方法,最后一个胜出。

自定义协程上下文

你可以传递一个自定义的协程上下文到监听器助手中:

button("Login") {

onClick(yourContext) {

val user = myRetrofitService.getUser().await()

showUser(user)

}

}

使用资源标识

之前的例子都是直接使用的Java字符串,当这在实践中不是一个好主意。 典型做法是将你所有的字符串数据都放到 res/values/ 文件夹,到运行时进行访问,例如: getString(R.string.login)。

幸运的是,在Anko中你可以将资源标识同时传递给blocks 助手 (button(R.string.login)) 和扩展属性 (button { textResource = R.string.login })。

注意:属性名称有点不太一样: 不是 text, hint, image,现在我们使用 textResource, hintResource 和 imageResource.

读取资源属性时允许抛出 AnkoException 。

实例速记符

有些时候,你需要传递一个 Context 实例到一些Android SDK的方法中。 通常情况下, 你可以仅仅使用 this, 但如果是在一个内部类中,该怎么办呢? 在Java中你会写 SomeActivity.this 如果你用的是Kotlin的话,则应该这样 this@SomeActivity 。

在kotlin中你可以只写 ctx。 这是一个工作在 Activity 和 Service 甚至是在 Fragment 中的扩展属性 (它背后使用的是 getActivity() 方法)。 你也可以使用扩展属性 act 获取 Activity 的实例。

UI wrapper

在早起的一段时间 Anko 使用 UI 标签作为顶级的 DSL 元素:

UI {

editText {

hint = "Name"

}

}

如果你喜欢的话,可以继续使用这个标签。这样可以更简单的扩展 DSL 你只需要声明一个 ViewManager.customView 函数。参考 Anko 扩展 for more information.

Include 标签

在 DSL 中插入一个XML布局十分的简单。使用 include() 函数:

include<View>(R.layout.something) {

backgroundColor = Color.RED

}.lparams(width = matchParent) { margin = dip(12) }

你可以像平常一样使用 lparams(),如果你提供一个 View 以外的类型,你也可以在 {} 中使用这个类型:

include<TextView>(R.layout.textfield) {

text = "Hello, world!"

}

Anko 支持插件

在IntelliJ IDEA 和 Android Studio 中可以使用Anko支持插件。它能帮你在IDE中,直接预览使用Anko写的AnkoComponent 。

Anko支持插件Android Studio 2.4及以上版本

安装Anko支持插件

你可以在这里下载Anko支持插件.

使用插件

保证你已经使用Anko写了这些类:

class MyActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {

super.onCreate(savedInstanceState, persistentState)

MyActivityUI().setContentView(this)

}

}

class MyActivityUI : AnkoComponent<MyActivity> {

override fun createView(ui: AnkoContext<MyActivity>) = ui.apply {

verticalLayout {

val name = editText()

button("Say Hello") {

onClick { ctx.toast("Hello, ${name.text}!") }

}

}

}.view

}

将鼠标放到 MyActivityUI 的声明中, 打开 Anko布局预览 工具窗口 ("View(视图)" → "Tool Windows(工具窗口)" → "Anko Layout Preview(Anko布局预览)") 并点击 Refresh(刷新).

这个过程需要构建工程,所以在看到图像之前需要等待一定的时间。

XML 转 DSL

这个插件运训你将XML格式的布局转换为Anko布局代码。 打开XML文件,并选择 "Code(代码)" → "Convert to Anko Layouts DSL"。 你可以同时转化多个XML文件。

Tags:

最近发表
标签列表