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)
            }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值