美文网首页Android技术知识Android开发经验谈Android开发
手把手教你搭建android模块化项目框架(十二)——实现自定义

手把手教你搭建android模块化项目框架(十二)——实现自定义

作者: 支离破碎_SuperLee | 来源:发表于2023-09-01 22:16 被阅读0次

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

    自定义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

    完整代码

    相关文章

      网友评论

        本文标题:手把手教你搭建android模块化项目框架(十二)——实现自定义

        本文链接:https://www.haomeiwen.com/subject/xaztvdtx.html