Anko学习

anko

Anko 是一个用 Kotlin 写的Android DSL (Domain-Specific Language)。 内部提供很多工具,详情可点https://github.com/Kotlin/anko
在这里我们主要涉及的是anko的layout部分,长久以来,Android视图都是用XML来完成布局的。用xml写布局有下面这些缺点:

  • 不是类型安全的;
  • 不是null-safe;
  • 强迫你对每个layout编写几乎相同的代码;
  • 在设备上解析XML费电费CPU;
  • 最惨的是代码不可复用
    所以,如果可以,我们可以尝试用anko来绘制布局。

环境配置

因为anko版本发布的并不是很多,用户量也不是很多,所有网上能找到的资源是非常少的,所以配置的时候总会出现许许多多的问题。现在android studio默认已经集成了kotlin了,我们需要配置的就是anko support,也就是anko preview,用来预览anko写的ui布局的工具。

插件安装

as联网下载

我们可以从android studio内置的下载器中下载,从Preference->Plugins中搜索Anko Support,并进行下载。
[图片上传失败...(image-8ed987-1542512015800)]

官网下载导入

我们也能够从官网中下载指定的anko support版本,点击https://plugins.jetbrains.com/plugin/7734-anko-support,选择指定版本进行下载,然后在as导入。
[图片上传失败...(image-770d0a-1542512015800)]
在android选择项目的页面,选择configuration->plugins->install plugin from disk,选择相应的plugin包导入。

可能出现的问题

目前官网最新的anko support插件版本是0.10.5,如果我们的as版本是3.0,那么还是将会出现anko preview不能预览的情况,报错情况为

Anko Support threw an uncaught NoSuchMethodError

这个是因为as3.0的问题不支持anko support,这个时候我们只需要更新as的版本到3.1之后,anko support就能够使用了。

开始使用anko

首先需要先引入anko依赖库

implementation "org.jetbrains.anko:anko:0.10.5"

这个是anko给我们集成的一个包含anko所有功能的库,包含了

  • anko commons: 一些常用的扩展函数,包括intent、dialog、log、resources等
  • ankoLayout: anko布局
  • ankoSqlite: anko优化的数据库操作
  • anko Coroutines: anko对于kotlin协程的扩展

如果我们需要大部分的anko的功能,我们可以直接这么引入,否则我们需要单独引入每一个功能的依赖。

dependencies {
    // Anko Commons
    implementation "org.jetbrains.anko:anko-commons:$anko_version"

    // Anko Layouts
    implementation "org.jetbrains.anko:anko-sdk25:$anko_version" // sdk15, sdk19, sdk21, sdk23 are also available
    implementation "org.jetbrains.anko:anko-appcompat-v7:$anko_version"

    // Coroutine listeners for Anko Layouts
    implementation "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"
    implementation "org.jetbrains.anko:anko-appcompat-v7-coroutines:$anko_version"

    // Anko SQLite
    implementation "org.jetbrains.anko:anko-sqlite:$anko_version"
}

我们只需要从中引入我们所必须的库就ok了。在这里我们需要注意的是anko-sdk的引入需要和我们build.gradle中的minSdkVersion做下适配。

org.jetbrains.anko:anko-sdk15 : 15 <= minSdkVersion < 19

org.jetbrains.anko:anko-sdk19 : 19 <= minSdkVersion < 21

org.jetbrains.anko:anko-sdk21 : 21 <= minSdkVersion < 23

org.jetbrains.anko:anko-sdk23 : 23 <= minSdkVersion < 25

org.jetbrains.anko:anko-sdk25 : 25 <= minSdkVersion 

如果没有按照要求做sdk适配,那么一旦运行在不符合的版本,将会直接崩溃~切记

AnkoComponent

我们可以在activity的onCreate、fragemnt的onCreateView中直接绘制ui,但是这样就让ui绘制代码�与activity、fragment代码有了耦合了。所有我们可以将Anko代码定义到另外一个class中,作为对应的UiClass,这就需要AnkoComponent。

定义UIClass

class MemberCenterActivityUI : AnkoComponent<MemberCenterActivity> {
        override fun createView(ui: AnkoContext<MemberCenterActivity>) : View = with(ui){
        
    }

在我们定义了相应的uiclass之后,我们需要实现其中的oncreateView方法,这个类的返回就是一个View,就是我们anko绘制的布局。
添加上with(ui),是为了让dsl代码块能够以this的形式持有AnkoContext,这个AnkoContext是一个非常有用的类

    val ctx: Context
    val owner: T
    val view: View

ctx就是上下文信息,owner就是uiclass依附的类的实例,在这个例子里面指的是MemberCenterActivity,我们可以通过owner调用MemberCenterActivity的任意public方法。

绘制布局

所有布局
            relativeLayout {
                imageView {
                    adjustViewBounds = true
                    scaleType = ImageView.ScaleType.CENTER_CROP
                    imageResource = R.drawable.bg_members
                }.lparams(width = matchParent, height = matchParent)
                statusBar = view {
                    id = statusBarHolder
                }.lparams {
                    height = statusBarHeight(ctx)
                    width = matchParent
                }
                toolbar {

                    id = toolbarId
                    backgroundColor = R.color.color_00000000

                    imageView {
                        imageResource = R.drawable.ic_arrow_back_white
                        onClick {
                            owner.finish()
                        }
                    }.lparams(height = wrapContent, width = wrapContent) {
                        padding = dip(5)
                    }

                    textView("会员中心") {
                        textColor = R.color.color_ffffff
                        textSize = R.dimen.dimens_18sp.toFloat()
                    }.lparams(width = wrapContent, height = wrapContent) {
                        gravity = Gravity.CENTER

                    }

                }.lparams(height = dip(50), width = matchParent) {
                    below(statusBar)
                }

                memberRv = recyclerView {
                    itemAnimator.changeDuration = 0
                    layoutManager = LinearLayoutManager(context)
                    adapter = MemberCenterAdapter(context, null)
                    memberAdapter = adapter as MemberCenterAdapter
                }.lparams(width = matchParent, height = matchParent) {
                    below(toolbarId)
                }
            }

每一个anko布局,最多只能有一个根节点,在这个例子中最外层的根节点就是relativelayout,这个组件的名称都是以小写字母开头的,就是anko给java的View多包装了一层,以便在dsl使用。

 imageView {
        ....
        }.lparams(width = matchParent, height = matchParent){
        .....
        }

控件直接包含的区域是添加控件的直接参数的,比如说imageView的src,scaleType,TextView的text,textSize,textColor等。而lparams是用来定义组件的大小,布局、位置属性的,包括height、width、gravity等等。
当然,我们也可以不指定lparams,这样的话,它会有默认的宽高,即wrap_content。

inline fun <T: View> T.lparams(
            width: Int = android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
            height: Int = android.view.ViewGroup.LayoutParams.WRAP_CONTENT
    ): T {
        val layoutParams = FrameLayout.LayoutParams(width, height)
        this@lparams.layoutParams = layoutParams
        return this
    }

整个imageView域是有返回值的,返回的就是这个imageView实体,我们也可以理解成这个域创建了imageView实体并返回。

anko扩展View

当我们想要用自定义View或者anko没有支持的View,我们可以扩展ankoView

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

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

这个mapView就是我们想要扩展的View,这个是官网给的例子,下面我们来扩展fresco的simpledraweeView

inline fun ViewManager.simpleDraweeView(theme: Int = 0) = simpleDraweeView(theme) {}
inline fun ViewManager.simpleDraweeView(theme: Int = 0, init: SimpleDraweeView.() -> Unit) : SimpleDraweeView {
    return ankoView({ SimpleDraweeView(it) }, theme, init)
}

这样我们就能在anko dsl中直接使用simpleDraweeView了

onclick

anko中已经给我们添加了onclick方法,我们可以在每个View中添加onClick,并且指定其实现,anko将会给这个View设置listener

fun android.view.View.onClick(
        context: CoroutineContext = UI,
        handler: suspend CoroutineScope.(v: android.view.View?) -> Unit
) {
    setOnClickListener { v ->
        launch(context) {
            handler(v)
        }
    }
}
一些小点
  • 用了anko之后,我们如果需要对于id的诉求只有一个,那就是在relativelayout中指定位置时,需要通过id,才指定below、above、left、right的位置,对于id的设置,我们需要自己定义Id的int值,我们得保证它不会重复。
  • 对于imageView,不再有src属性,因为anko内部其实是动态代码创建view,所有对于这个src,替代的就是在代码中指定的imageResouce
  • 对于color,需要的就是color的值,而非项目中的colorId
  • 对于textView,它的textSize指定为f,而非sp
  • 如果我们想要用recyclerView,那么我们需要引入anko的依赖库,或者直接扩展ankoView
  • 对于dp计算,anko内部给我们实现了dip方法计算。

anko preivew

对于anko support,虽然可能它的代码效率比较高,但是它在预览的时候并没有办法做到像在xml里面实时预览,只有在代码build之后才能够看到预览页面,而且每次修改都必须要进行rebuild,这个是一个非常让人无法接受的点,刚开始效率肯定会低,但是随着代码的熟练,效率还是会有所提升的,毕竟anko的ui绘制效率要笔xml高。可以通过command+f9进行build。

ankoComponent元素的调用

我们免不了需要在activity,framgent等类中更新uiClass的元素的状态。所以了解uiClass与activity,fragment及viewHolder的绑定及调用还是有必要的。

绑定
activity

activity绑定ankoComponent可以通过下面这种方式来绑定

MemberCenterActivityUI().setContentView();
fragment
val view = MemberCenterFragmentUI().createView(AnkoContext.create(ctx, MemberCenterFragment()))

在onCreateView中返回这个View,就和fragment进行绑定了。

viewHolder
MemberCenterItemUI().createView(AnkoContext.create(context, parent)
自定义View

在自定义View中,对于anko来说是一个比较不方便的使用,因为在anko support的支持预览的前提是使用AnkoComponent,而自定义View的View绑定却不通过AnkoComponent,也有可能是我现在没发现,目前发现的支持绑定的方式为:

  constructor(context: Context) : super(context) {
        initView()
    }
 private fun initView() = AnkoContext.createDelegate(this).apply {
    ...init anko view
}

在java的自定义View中,是通过构造函数调用inflate方法,来将xml设置到该自定义View中。
在anko中,利用AnkoContext.createDelegate(this).apply来替代inflate。

我们可以先通过AnkoComponent来预览界面,然后预览完成之后再将anko代码放在AnkoContext.createDelegate(this).apply中。

调用

对于三者的调用其实都是一致的,我们只需要缓存AnkoComponent类的实例就能够通过这个实例引用其内部的ui组件。

//in activity
mainUI = MemberCenterActivityUI()
    mainUI.setContentView(this)
    
 TextUtils.isEmpty(chargeTip?.chargeButtonDesc).yes {
                mainUI.bottomMemberOpenDesc.visibility = View.GONE
            }.no {
                mainUI.bottomMemberOpenDesc.visibility = View.VISIBLE
                mainUI.bottomMemberOpenDesc.text = chargeTip?.chargeButtonDesc
            }
            TextUtils.isEmpty(chargeTip?.chargeButtonUrl).yes {
                mainUI.btnOpenViewIcon.visibility = View.GONE
            }.no {
                mainUI.btnOpenViewIcon.visibility = View.VISIBLE
                KKGifPlayer
                        .with(this)
                        .load(chargeTip?.chargeButtonUrl)
                        .playPolicy(KKGifPlayer.PlayPolicy.Auto_Always)
                        .into(openViewIcon)
            }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容