Anko demo(二) :CustomView 首次尝试

本文介绍如何使用Anko DSL封装TitleBar组件,包括自定义视图添加到DSL中的方法及实现动态设置返回按钮图标与标题内容的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

直接上图

这里写图片描述

目标

将 TitleBar 封装起来,从页面上来说就是将 返回按钮 和 title 封装成一个组合控件

stackoverflow

how-to-add-custom-view-groups-to-anko-dsl

动手实践

难点有2 –>
1. kotlin 形式的 inflate view 写法
2. 如何将自定义 View 添加到 DSL 中使用

难点解决: kotlin 形式的 inflate view 写法

参考 github demo anko-example

上代码:

class KtTitleBar : FrameLayout {

    private lateinit var image: ImageView
    private lateinit var text: TextView

    private lateinit var imageParent: LinearLayout

    constructor(context: Context?) : super(context) {
        initView()
    }

    private fun initView() = AnkoContext.createDelegate(this).apply {
        relativeLayout {
            gravity = Gravity.CENTER_VERTICAL

            imageParent = verticalLayout {
                image = imageView(R.drawable.icon).lparams(dip(21), dip(21)) {
                    padding = dip(12)
                }
            }

            text = textView {
                textSize = 18f
                textColor = Color.parseColor("#333333")
            }.lparams {
                centerInParent()
            }
        }
    }
}

(ps: 事实上,在上面的代码中 imageParent 这个父节点是有点多余的,但是,我在自定义 View 中发现使用 padding 的效果居然和 margin 的效果是等同的,所以我只能添加 imageParent。我觉得这是个坑,解释详见 Anko 我遇到的坑


难点解决: 如何添加到 DSL 中使用

如上代码控件最基本的代码已经完成了,那么接下来就是怎么在 DSL 中使用,如果你仔细看过了 demo 中的 customView 代码,你就会发现完整的代码应该是这样的:

package kt.widget

import android.content.Context
import android.graphics.Color
import android.view.Gravity
import android.view.ViewManager
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import com.ibplus.client.R
import com.ibplus.client.Utils.RxUtil
import org.jetbrains.anko.*
import org.jetbrains.anko.custom.ankoView

/**
 * Created by cjh on 2017/6/14.
 */
class KtTitleBar : FrameLayout {

    private lateinit var image: ImageView
    private lateinit var text: TextView

    private lateinit var imageParent: LinearLayout

    constructor(context: Context?) : super(context) {
        initView()
    }

    private fun initView() = AnkoContext.createDelegate(this).apply {
        relativeLayout {
            gravity = Gravity.CENTER_VERTICAL

            imageParent = verticalLayout {
                image = imageView(R.drawable.icon).lparams(dip(21), dip(21)) {
                    padding = dip(12)
                }
            }

            text = textView {
                textSize = 18f
                textColor = Color.parseColor("#333333")
            }.lparams {
                centerInParent()
            }
        }
    }
}
inline fun ViewManager.ktTitleBar(theme: Int = 0) = ktTitleBar({}, theme)
inline fun ViewManager.ktTitleBar(init: ktTitleBar.() -> Unit, theme: Int = 0) = ankoView(::ktTitleBar, theme, init)

代码到这里,实际上已经可以在 DSL 中使用了,但是也仅仅只是引用的层次,比如我想动态的改变它的 backIcon,我想改变 title 的内容,其实主要理由是,写到这里,在 DSL 中只能这么用:

verticalLayout {
     ktTitleBar()
}

连最基本的这样使用都不行:

verticalLayout {
     ktTitleBar(){

     }
}

那么记下来我就要来完善我的 TitleBar 了。

首先是 DSL 的使用优化,我非常希望这样使用:
verticalLayout {
     ktTitleBar{
         ......
     }
}

那么代码改动就是这样:

从 –>

inline fun ViewManager.ktTitleBar(theme: Int = 0) = ktTitleBar({}, theme)
inline fun ViewManager.ktTitleBar(init: ktTitleBar.() -> Unit, theme: Int = 0) = ankoView(::ktTitleBar, theme, init)

变成 –>

inline fun ViewManager.ktTitleBar() = ktTitleBar{}
inline fun ViewManager.ktTitleBar(init: KtTitleBar.() -> Unit) = ankoView(::KtTitleBar, 0, init)
继续深入自定义

以上的代码保证了基本的使用,但是在实际开发中,TitleBar 的返回按钮的图标和标题一定不会是一成不变的,它们随着页面的不同而改变,所以在实际开发中,我希望的使用代码模式是这样的:

verticalLayout {
     ktTitleBar(backIcon, title){
         ......
     }
}

以上代码的所表达的其实就是多变的项目使用。


那么最终我的 TitleBar 的完整代码变成了这样:

package kt.widget

import android.content.Context
import android.graphics.Color
import android.view.Gravity
import android.view.ViewManager
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import com.ibplus.client.R
import com.ibplus.client.Utils.RxUtil
import com.ibplus.viewtoimage.LogUtils
import org.jetbrains.anko.*
import org.jetbrains.anko.custom.ankoView

/**
 * Created by cjh on 2017/6/14.
 */
class KtTitleBar : FrameLayout {

    private lateinit var image: ImageView
    private lateinit var text: TextView

    private lateinit var imageParent: LinearLayout

    constructor(context: Context?) : super(context) {
        initView()
    }


    private fun initView() = AnkoContext.createDelegate(this).apply {
        relativeLayout {
            gravity = Gravity.CENTER_VERTICAL

            imageParent = verticalLayout {
                LogUtils.e("createDelegate")
                image = imageView(R.drawable.menu_icon_back).lparams(dip(21), dip(21)) {
                    padding = dip(12)
                }
            }

            text = textView {
                textSize = 18f
                textColor = Color.parseColor("#333333")
            }.lparams {
                centerInParent()
            }
        }
    }

    fun setBackIcon(res: Int) {
        image?.setImageResource(res)
    }

    fun setTitle(title: String) {
        text?.setText(title)
    }

    fun setBackIconClickFun(click: () -> Unit) {
        RxUtil.click(imageParent, click)
    }
}


inline fun ViewManager.ktTitleBar() = ktTitleBar {}
inline fun ViewManager.ktTitleBar(init: KtTitleBar.() -> Unit) = ankoView(::KtTitleBar, 0, init)
inline fun ViewManager.ktTitleBar(backIcon: Int, title: String, init: KtTitleBar.() -> Unit) = ktTitleBar {
    setBackIcon(backIcon)
    setTitle(title)
}

撇去添加的几个扩展功能性的函数 setBackIcon / setTitle / setBackIconClickFun,主要看控件的定义,我添加了:

inline fun ViewManager.ktTitleBar(backIcon: Int, title: String, init: KtTitleBar.() -> Unit) = ktTitleBar {
    setBackIcon(backIcon)
    setTitle(title)
}

这样我在 DSL 中使用的时候就变成如下的方式了:

 val _ktTitleBar = ktTitleBar(backIcon, title) {
     backgroundColor = Color.WHITE
 }
_ktTitleBar.setBackIconClickFun { ui.owner.finish() }

至此,一个非常简单的 Anko TitleBar 就完成了,其实完成的比较曲折,因为官方对这一块的描述相当不完整,几乎没有,所以基本是一路 stackoverflow/github/看源码/猜测 走过来的。另外,写完之后,我发现 anko 真的是想和 xml 说 88,具体原因见:Anko 我遇到的坑

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值