手把手教你搭建android模塊化項目框架(十二)——實現(xiàn)自定義view的一些小技巧~

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

自定義view怎么實現(xiàn),我今天不想多說,畢竟也不是給新人看的。

那么今天直接講一些實現(xiàn)自定義view的小技巧吧。

本期舉例的自定義view只是拋磚引玉,隨手寫的沒有經(jīng)過測試,如果想使用一定要三思而后行~

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)

那么我們直接看優(yōu)化后的代碼~

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

mBinding.tvTab.xxxxxxxx
//直接使用,節(jié)省時間。
  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>

在創(chuàng)建view的構(gòu)造函數(shù)中填入默認style,然后其他與正常寫自定義view就一樣啦~

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

如果我們想動態(tài)加入主題呢?可以在自定義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中接受自定義參數(shù)例如:

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

然后獲?。?/p>

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

之后在Build子view時,將themeId傳入即可~

當然,寫法有很多,本篇僅僅是拋磚引玉而已。

  1. dsl構(gòu)建view參數(shù)

先看效果~ 可以是這樣的

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看起來復(fù)雜,其實很簡單,例如option構(gòu)建時我們多寫一些方法~

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()
    }
}

這樣就可以使用高階函數(shù)進行構(gòu)建了,配合kotlin的lambda特性即可達到效果~

當然,為了看起來更舒適,也少不了我們的擴展函數(shù)啦~

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

至此,我們便完成了一個優(yōu)雅的自定義view

完整代碼

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容