Kotlin 结合 Anko 编写布局
什么是Anko?
Anko是由 Android Studio 的开发公司 jetBrains 官方出品的针对Kotlin语言,并且使开发Android程序更高效更简单的库,它可以使代码更简洁优雅。
它可以分为几个部分:
- Anko Commons: 这个里面包括很多使用的工具,可以帮助我们减少代码量,包括intents ,dialog,log,还有好多....
- Anko Layouts: 是一种快速,type-safe的动态 Android 布局方案
- Anko SQLite: 帮助查询SQLite的DSL 解析器集合
- Anko Coroutines:基于kotlin 协程的扩展工具
为什么要使用Anko来写布局呢?
在常规的Andorid开发中,我们都是用XML写布局,在这几个方面有点不方便:
- 不是type-safe的;
- 不是null-safe的;
- 每个布局文件都要写好多重复的代码
- 在Android设备上解析XML会更加耗时和耗电
- 不能重用
而且当我们想用代码直接创建UI的时候,会使代码看起来很难维护,并且不够优雅。
val activity = this
val layout = LinearLayout(act)
layout.orientation = LinearLayout.VERTICAL
val button = Button(act)
button.text = "退出登录"
button.setOnClickListener {
logout()
}
layout.addView(button)
上面是动态添加一个退出按钮的操作,已经是kotlin中简化的代码了,可以说是又丑可读性也差,如果用java写的话会更长。如果用Anko的DSL方式编写会是什么样呢?
verticalLayout {
button("退出登录") {
onClick { logout() }
}
}
一个普通的设置界面
如果我们想要完成上面这样的界面,正常情况的XML是这样的:
image.png
首先是一个垂直的线性布局,里面一个自定义的Title,之后是五个 RelativeLayout, 最后是一个Button,那么如果利用Anko来进行编写是什么样的呢?
首先 创建一个实现 AnkoComponent
接口的类(虽然可以直接在onCreate里面用DSL写,但是如果用这个 AnkoComponent
,然后再安一个 Anko Support
的 插件 就有几率可以预览界面,但是经过实测,其实并不是那么好用 ,每次编辑完都要 build 一下才能预览 ,还不如直接在设备上调试。)
class SettingActivityUI(private val activity: BaseActivity) : AnkoComponent<SettingAnkoActivity> {
override fun createView(ui: AnkoContext<SettingAnkoActivity>): View = with(ui) {
verticalLayout {
backgroundResource = R.color.bg_color
commonTitle {
setLeftBackgroundResource(R.drawable.ic_return)
setTitle("设置")
setLeftClick { activity.finish() }
}.lparams(width = matchParent, height = wrapContent)
relativeLayout {
backgroundResource = R.color.white
textView {
id = ID_title
text = "个人设置"
textColorResource = R.color.c_333333
textSize = 15f
val leftDrawable = activity.resources.getDrawable(R.drawable.geren_icon)
leftDrawable.setBounds(0, 0, leftDrawable.minimumWidth, leftDrawable.minimumWidth)
setCompoundDrawables(leftDrawable, null, null, null)
compoundDrawablePadding = 16
}.lparams(wrapContent, wrapContent) {
centerVertically()
alignParentLeft()
setMargins(dip(16), 0, 0, 0)
}
imageView {
setImageResource(R.drawable.jiantou_you)
}.lparams(width = wrapContent, height = wrapContent) {
alignParentRight()
centerVertically()
setMargins(0, 0, dip(16), 0)
}
}.lparams(matchParent, dip(50)).onClick {
find<TextView>(ID_title).text = "点击了个人设置"
// mBundle?.putString("sex", sex)
// IntentUtils.startActivity(activity, UserSettingActivity::class.java, mBundle)
}
relativeLayout {
backgroundResource = R.color.white
textView {
text = "意见反馈"
textColorResource = R.color.c_333333
textSize = 15f
val leftDrawable = activity.resources.getDrawable(R.drawable.yijian_icon)
leftDrawable.setBounds(0, 0, leftDrawable.minimumWidth, leftDrawable.minimumWidth)
setCompoundDrawables(leftDrawable, null, null, null)
compoundDrawablePadding = 16
}.lparams(wrapContent, wrapContent) {
centerVertically()
alignParentLeft()
setMargins(dip(16), 0, 0, 0)
}
imageView {
setImageResource(R.drawable.jiantou_you)
}.lparams(width = wrapContent, height = wrapContent) {
alignParentRight()
centerVertically()
setMargins(0, 0, dip(16), 0)
}
}.lparams(matchParent, dip(50)).onClick {
IntentUtils.startActivity(activity, FeedbackActivity::class.java)
}
relativeLayout {
backgroundResource = R.color.white
textView {
text = "帮助中心"
textColorResource = R.color.c_333333
textSize = 15f
val leftDrawable = activity.resources.getDrawable(R.drawable.help_icon)
leftDrawable.setBounds(0, 0, leftDrawable.minimumWidth, leftDrawable.minimumWidth)
setCompoundDrawables(leftDrawable, null, null, null)
compoundDrawablePadding = 16
}.lparams(wrapContent, wrapContent) {
centerVertically()
alignParentLeft()
setMargins(dip(16), 0, 0, 0)
}
imageView {
setImageResource(R.drawable.jiantou_you)
}.lparams(width = wrapContent, height = wrapContent) {
alignParentRight()
centerVertically()
setMargins(0, 0, dip(16), 0)
}
}.lparams(matchParent, dip(50)).onClick {
IntentUtils.startActivity(activity, HelpActivity::class.java)
}
relativeLayout {
backgroundResource = R.color.white
textView {
text = "安全中心"
textColorResource = R.color.c_333333
textSize = 15f
val leftDrawable = activity.resources.getDrawable(R.drawable.anquan_icon)
leftDrawable.setBounds(0, 0, leftDrawable.minimumWidth, leftDrawable.minimumWidth)
setCompoundDrawables(leftDrawable, null, null, null)
compoundDrawablePadding = 16
}.lparams(wrapContent, wrapContent) {
centerVertically()
alignParentLeft()
setMargins(dip(16), 0, 0, 0)
}
imageView {
setImageResource(R.drawable.jiantou_you)
}.lparams(width = wrapContent, height = wrapContent) {
alignParentRight()
centerVertically()
setMargins(0, 0, dip(16), 0)
}
}.lparams(matchParent, dip(50)).onClick {
IntentUtils.startActivityForResult(activity, SecurityCenterActivity::class.java, mBundle, 100)
}
relativeLayout {
backgroundResource = R.color.white
textView {
text = "当前版本"
textColorResource = R.color.c_333333
textSize = 15f
val leftDrawable = activity.resources.getDrawable(R.drawable.aboutme_icon)
leftDrawable.setBounds(0, 0, leftDrawable.minimumWidth, leftDrawable.minimumWidth)
setCompoundDrawables(leftDrawable, null, null, null)
compoundDrawablePadding = 16
}.lparams(wrapContent, wrapContent) {
centerVertically()
alignParentLeft()
setMargins(dip(16), 0, 0, 0)
}
textView {
text = "v.${BuildConfig.VERSION_NAME}"
textColor = Color.parseColor("#999999")
textSize = 14f
}.lparams(width = wrapContent, height = wrapContent) {
alignParentRight()
centerVertically()
setMargins(0, 0, dip(16), 0)
}
}.lparams(matchParent, dip(50)) {
topMargin = dip(16)
}.onClick {
toast("正在检测,请稍后")
VersionUpdateUtils
.newInstance()
.latestVersionIndex(activity, activity.supportFragmentManager, "1")
}
button {
backgroundResource = R.drawable.shape_btn_bg
text = "退出当前账号"
textColorResource = R.color.white
}.lparams(dip(300), wrapContent) {
topMargin = dip(30)
gravity = Gravity.CENTER
}.onClick {
activity.alert {
with(activity){
mApp.spUtils.putString(Constants.CCUSERID, "")
mApp.spUtils.putString(Constants.TOKEN, "")
val intent = Intent(this, MainActivity::class.java)
intent.putExtra("flag", "exit")
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
startActivity(intent)
finish()
}
}.show()
}
}
}
}
}
上面的代码中有一个自定义的view:CommonTileView,这个在Anko中是没有支持的,所以要怎么才能把自己写好的View也支持Anko呢?
我们只需要在任何kt文件中写下扩展就好了:
inline fun ViewManager.commonTitle(): CommonTileView = commonTitle {}
inline fun ViewManager.commonTitle(init: (@AnkoViewDslMarker CommonTileView).() -> Unit): CommonTileView {
return ankoView(
{ CommonTileView(it) },
theme = 0,
init = init
)
}
然而~~! 如果你不想凭借自己的想象写Anko的布局,那么你可以在安装了 Anko Support
插件之后 ,通过转换编写完的XML文件 ,自动生成 Anko Layouts的代码 在XML界面 ,点击工具栏上面的 ‘Code’ --> 'Convert to Anko Layouts DSL' ,这样就可以转换大部分XML的代码,在通过一丢丢的修改,就可以了,非常简单。