手把手教你搭建android模块化项目框架(十二)——实现自定义view的一些小技巧~

原来今天才是周六~那就今天水

自定义view怎么实现,我今天不想多说,毕竟也不是给新人看的。

那么今天直接讲一些实现自定义view的小技巧吧。

本期举例的自定义view只是抛砖引玉,随手写的没有经过测试,如果想使用一定要三思而后行~

1.利用databinding或者viewbinding,告别如下代码~

animView = findViewById(R.id.anim_view)
iconView = findViewById(R.id.iv_tab)
textView = findViewById(R.id.tv_tab)
badgeView = findViewById(R.id.iv_badge)

那么我们直接看优化后的代码~

private val mBinding by lazy {
    ViewMainBottomLayoutBinding.inflate(
        LayoutInflater.from(context),
        this,
        true
    )
}

mBinding.tvTab.xxxxxxxx
//直接使用,节省时间。
  1. 让你的自定义view支持style,方便使用

首先看我们的自定义属性

<declare-styleable name="BottomNavigationView">
    <attr name="iconWidth" format="dimension" />
    <attr name="iconHeight" format="dimension" />
    <attr name="textSize" format="dimension" />
    <attr name="textColor" format="color" />
</declare-styleable>

然后定义默认style

<style name="BottomNavigationViewStyle">
    <item name="iconWidth">30dp</item>
    <item name="iconHeight">30dp</item>
    <item name="textColor">@color/color_bottom_nav_view_text_default</item>
    <item name="textSize">12sp</item>
</style>

在创建view的构造函数中填入默认style,然后其他与正常写自定义view就一样啦~

constructor(context: Context, attrs: AttributeSet?) : super(
    context,
    attrs,
    R.style.BottomNavigationViewStyle
) {
    setupAttr(attrs)
}

如果我们想动态加入主题呢?可以在自定义view中添加setTheme方法,然后取值方式如下,可能还有其他取值方式~不过懒得找了。

fun setTheme(themeId: Int) {
    val mTheme = context.resources.newTheme()
    mTheme.applyStyle(themeId, true)
    mTheme.obtainStyledAttributes(
        intArrayOf(
            R.attr.iconWidth,
            R.attr.iconHeight,
            R.attr.textColor,
            R.attr.textSize
        )
    ).run {
        iconWidth =
            this.getDimensionPixelSize(this.getIndex(0), iconWidth)
        iconHeight =
            this.getDimensionPixelSize(this.getIndex(1), iconHeight)
        textColor =
            this.getColorStateList(this.getIndex(2)) ?: textColor
        textSize = this.getDimension(this.getIndex(3), textSize)
        recycle()
    }
    setup()
}

如此,我们便可以直接配置style给自定义view啦~由于本demo使用的是组合view,所以我们可以在父view中接受自定义参数例如:

<declare-styleable name="BottomNavigationGroup">
    <attr name="navBottomViewStyle" format="reference" />
</declare-styleable>

然后获取:

context.obtainStyledAttributes(attrs, R.styleable.BottomNavigationGroup).run {
    navViewThemeId =
        getResourceId(R.styleable.BottomNavigationGroup_navBottomViewStyle, navViewThemeId)
    recycle()
}

之后在Build子view时,将themeId传入即可~

当然,写法有很多,本篇仅仅是抛砖引玉而已。

  1. dsl构建view参数

先看效果~ 可以是这样的

mBinding.homeTab.setup {
    options(
        bottomNavOption {
            id { R.id.home }
            tabText { "home" }
            iconRes { R.drawable.ic_main_nav_home }
        },
        bottomNavOption {
            id { R.id.topic }
            tabText { "topic" }
            iconRes { R.drawable.ic_main_nav_home }
        },
        bottomNavOption {
            id { R.id.find }
            tabText { "find" }
            iconRes { R.drawable.ic_main_nav_home }
        },
        bottomNavOption {
            id { R.id.me }
            tabText { "me" }
            iconRes { R.drawable.ic_main_nav_home }
        }
    )
    listener {
        object : BottomNavigationGroup.OnCheckedChangeListener {
            override fun onCheckedChanged(group: BottomNavigationGroup?, checkedId: Int) {

            }

        }
    }
    defaultChecked {
        R.id.home
    }
}

也可以是这样的~

mBinding.homeTab.setup {
    options(
        bottomNavOption {
            id { R.id.home }
            tabText { "home" }
            iconRes { R.drawable.ic_main_nav_home }
        })
    options(bottomNavOption {
        id { R.id.topic }
        tabText { "topic" }
        iconRes { R.drawable.ic_main_nav_home }
    })
    options(
        bottomNavOption {
            id { R.id.find }
            tabText { "find" }
            iconRes { R.drawable.ic_main_nav_home }
        })
    bottomNavOption {
        id { R.id.me }
        tabText { "me" }
        iconRes { R.drawable.ic_main_nav_home }
    }
    )
    listener {
        object : BottomNavigationGroup.OnCheckedChangeListener {
            override fun onCheckedChanged(group: BottomNavigationGroup?, checkedId: Int) {

            }

        }
    }
    defaultChecked {
        R.id.home
    }
}

当然,写法有很多,本文最终提交的是第一种的写法~

这个dsl看起来复杂,其实很简单,例如option构建时我们多写一些方法~

class Option {
    @IdRes
    var id: Int = -1
        private set
    var tabText: String = ""

    @DrawableRes
    var iconRes: Int = 0
        private set

    var textColor: ColorStateList? = null
        private set

    var iconW: Int = 0
        private set

    var iconH: Int = 0
        private set

    var textSize: Float = 0f
        private set

    fun id(init: () -> Int) {
        id = init()
    }

    fun tabText(init: () -> String) {
        tabText = init()
    }

    fun iconRes(init: () -> Int) {
        iconRes = init()
    }

    fun textColor(init: () -> Int) {
        textColor = ResourceUtil.getColorStateList(resId = init())
    }

    fun iconW(init: () -> Int) {
        iconW = init()
    }

    fun iconH(init: () -> Int) {
        iconH = init()
    }

    fun textSize(init: () -> Float) {
        textSize = init()
    }
}

这样就可以使用高阶函数进行构建了,配合kotlin的lambda特性即可达到效果~

当然,为了看起来更舒适,也少不了我们的扩展函数啦~

fun bottomNavOption(init: BottomNavigationView.Option.() -> Unit): BottomNavigationView.Option {
    val option = BottomNavigationView.Option()
    option.init()
    return option
}

至此,我们便完成了一个优雅的自定义view

完整代码

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

推荐阅读更多精彩内容