前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Kotlin 自定义 标签viewgroup

Kotlin 自定义 标签viewgroup

作者头像
stormKid
发布2018-09-12 15:41:41
1.2K0
发布2018-09-12 15:41:41
举报
文章被收录于专栏:计算机编程计算机编程

android 对于kotlin语言做了强调转移过后,kotlin逐渐取代java,成为Android开发语言中极为重要的语言之一。涉及到kotlin语法的相关知识我就不多说了,今天就项目需求,自定义一个viewgroup作标签视图来使用进项目中去。

1、开写继承constructor

一般在java语言中,constructor直接在继承viewgroup后会报错,然后根据自定义快捷键,默认为alt+enter【博主是用的eclipse 的keymap所以采用的 ctrl+/】就提示出来让你选择了。

点击图示选项.png

点击上图所示,其就会进入选择项:

选择条目.png

选择1、2、3行进行复写,然后就写其他自定义逻辑就完了。然而到了kotlin它的constructor很特别,可以根据语法如下书写:

代码语言:javascript
复制
class Test constructor (private val context:Context) : ViewGroup(context){}

如此这般如何复写三个constructor呢,实际上也很简单:

实现复写constructor.png

2、核心两方法思路与实现:

2.1、onMesure() 根据子控件来计算父控件的大小:

代码语言:javascript
复制
    /**
     * 计算子控件大小进行自动换行处理
     */
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)

        val sizeWidth = MeasureSpec.getSize(widthMeasureSpec)
        val sizeHeight = MeasureSpec.getSize(heightMeasureSpec)
        val modeWidth = MeasureSpec.getMode(widthMeasureSpec)
        val modeHeight = MeasureSpec.getMode(heightMeasureSpec)

        //初始化父控件大小
        var resultWidth = 0
        var resultHeight = 0


        // 初始化行控件大小
        var itemWidth = 0
        var itemHeight = 0

        for (i in 0 until childCount) { // 遍历 所有的子元素
            val child = getChildAt(i)
            val layoutParams = child.layoutParams as MarginLayoutParams
            measureChild(child, widthMeasureSpec, heightMeasureSpec) // 先测量

            //计算所有的子控件宽高
            val childWidth = child.measuredWidth
            val childHeight = child.measuredHeight
            // 通过margin计算所有子控件实际加上margin值的大小
            val realWidth = childWidth + layoutParams.leftMargin + layoutParams.rightMargin
            val realHeight = childHeight + layoutParams.topMargin + layoutParams.bottomMargin

            if (sizeWidth < (itemWidth + realWidth)) {//换行
                resultWidth = Math.max(realWidth, itemWidth)
                resultHeight += realHeight
                itemHeight = realHeight
                itemWidth = realWidth
            } else { // 添加
                itemWidth += realWidth
                itemHeight = Math.max(realHeight, itemHeight)
            }

            // 最后一行不换行
            if (i == childCount - 1) {
                resultWidth = Math.max(realWidth, itemWidth)
                resultHeight += itemHeight
            }
            // 通过判断本自定义控件width||height 的属性是否为warp_content来进行给此view赋值,如果为march_parent或者其他属性,则使用其他属性定义的宽高值
            // 如果仅为wrap_content则使用计算后的宽高给父控件赋值
            setMeasuredDimension(if (modeWidth == View.MeasureSpec.EXACTLY) sizeWidth else resultWidth,
                    if (modeHeight == View.MeasureSpec.EXACTLY) sizeHeight else resultHeight)
        }
    }

通过以上方法我们控制了子view的显示,同时让我们现在的viewgroup的宽高在程序中可以进行控制处理,不会让视图错乱。

2.2、onLayout()

代码语言:javascript
复制
  /**
     * 控制子view所在的位置
     */
    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        // 初始化子控件位置
        var pointWidth = 0
        var pointHeight = 0

        (0 until childCount)
                .asSequence() //序列化
                .map { getChildAt(it) }//开始遍历子控件
                .filter { it.visibility != View.GONE } // 过滤view为gone的控件
                .forEach {
                    // 获取子控件的测量宽高
                    val childHeight = it.measuredHeight
                    val childWidth = it.measuredWidth
                    // 使用margin的params作为定位器
                    val layoutParams = it.layoutParams as MarginLayoutParams
                    // 判断是否换行。
                    if (pointWidth + childWidth + layoutParams.leftMargin + layoutParams.rightMargin > width) {
                        pointHeight += childHeight + layoutParams.topMargin + layoutParams.bottomMargin
                        pointWidth = 0
                    }
                    // 计算控件绘图定位
                    val top = layoutParams.topMargin + pointHeight
                    val bottom = layoutParams.bottomMargin + pointHeight + childHeight
                    val left = layoutParams.leftMargin + pointWidth
                    val right = layoutParams.rightMargin + pointWidth + childWidth
                    it.layout(left, top, right, bottom)
                    // 通过view的tag来显示view的实际变化
                    val tag = it.tag as CateGroyBean
                    if (tag.isChoose) it.setBackgroundResource(R.drawable.shape_selected)
                    else it.setBackgroundResource(R.drawable.shape_no_select)
                    //记录最终view的位置
                    pointWidth += layoutParams.leftMargin + childWidth + layoutParams.rightMargin
                }

    }

通过onLayout方法记住了子view的位置,直接底层绘图处理定位每个子元素的位置,并可让子view通过自己设定的方式进行显示。

3、控制子view的点击与显示

在使用angular过后明白了一点,数据绑定耐前端开发人员最核心最核心的思想,于是我们这里可以借鉴angular的数据绑定思想来控制我们的view的高亮显示:

赋值多种操作方式.png

通过数据绑定方式来控制点击视图变化.png

这里结合前面的onLayout方法,将数据的bean作为一个tag赋值给对应的子view上,于是每个子view拥有了此数据的属性,我们可以根据控制每个子view的点击状态改变绑定的数据,从而控制了整个视图的变化。

4、屏幕适配

在这里我自定义了几种属性:

几种自定义属性.png

由于本身根据子控件进行测量显示,子控件只需要控制textview的textsize就可以实现不同屏幕的适配了,这里我封装了一个textview屏幕适配的类:DimenUtil。

DimenUtil 根据屏幕宽度的百分比来设定本textview的字体大小,textview字体可以看作是正方形模块,只要限定住了百分比就可以控制了它的适配,它也采取了单例的模式进行使用,无需额外的操作,使用也非常简单。

DimenUtil部分代码.png

说明:推荐使用默认配置达到最好的适配效果

5、最终效果

普通选定效果.gif

单选效果.gif

多选效果.gif

查看使用方式及例子请点击此处

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.03.05 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、开写继承constructor
  • 2、核心两方法思路与实现:
  • 3、控制子view的点击与显示
  • 4、屏幕适配
  • 5、最终效果
  • 查看使用方式及例子请点击此处
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档