美文网首页
TabLayout背景

TabLayout背景

作者: 有点健忘 | 来源:发表于2020-09-18 10:46 被阅读0次

    基础知识

    com.google.android.material.tabs.TabLayout
    在材料主题下,默认是有个背景颜色的,用的colorSurface【默认白色】

    实现如下效果

    忘了那个帖子哪里看到的了,需求就是这个样子,弧线


    image.png
    image.png
    image.png

    原贴作者是自定义了一个View,我们这里偷懒了,直接继承TabLayout,只是在监听到tab切换的时候修改下背景即可,保留TabLayout的所有属性.
    弧线就是个三阶贝塞尔曲线,中间两个黑点就是控制点,两个蓝点是起点和终点


    image.png

    分析下,有3种情况,第一个只有右半边,最后一个只有左半边,中间的两边都有.
    在容器大小确定以后,这3种其实都可以画出来的,额,就是path可以计算出来了,当然了,中间那个位置还不确定,我们可以先默认中间的位置就在(0,0)的位置,后边根据实际需求移动一下就完事了
    所以在onLayout里把这3种path都计算出来,后边用到就直接拿来用了

        override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
            super.onLayout(changed, left, top, right, bottom)
            if(tabCount==0){
                return
            }
            delet=height/4f//我这里用了高度的1/4,自己可以适当修改
            val y=height.toFloat()
    
            pathTotal.reset()//这就是整个容器的矩形path
            pathTotal.addRect(0f,0f,width.toFloat(),y,Path.Direction.CW)
    
            var normalX0=width*1f/tabCount;
            pathFirst.reset()
            pathFirst.moveTo(0f,0f)
            pathFirst.lineTo(normalX0-delet,0f)
            pathFirst.cubicTo(normalX0,0f,normalX0,height.toFloat(),normalX0+delet,height.toFloat())
            pathFirst.lineTo(0f,height.toFloat())
            pathFirst.close()
    
            val normalXN=width-normalX0
    
            pathLast.reset()
            pathLast.moveTo(normalXN+delet,0f)
            pathLast.cubicTo(normalXN,0f,normalXN,y,normalXN-delet,y)
            pathLast.lineTo(width.toFloat(),y)
            pathLast.lineTo(width.toFloat(),0f)
            pathLast.close()
    
            //path center,temp location at (0,0)
            pathCenter.reset()
            pathCenter.moveTo(delet,0f)
            pathCenter.cubicTo(0f,0f,0f,y,-delet,y)
            pathCenter.lineTo(normalX0+delet,y)
            pathCenter.cubicTo(normalX0,y,normalX0,0f,normalX0-delet,0f)
            pathCenter.close()
    
            updateBg()
        }
    

    如果tab有4个5个,那么我们要画这么多吗?不用,我们只需要知道选中的那个path,然后用整个容器的矩形path 把选中的path去掉就ok了
    pathSelect就是选中的tab背景path,
    剩余部分的path就是利用Path.FillType.EVEN_ODD 从pathTotal里把上边的pathSelect抠掉就完事了.

        private fun checkSelectPath(){
            pathSelect.reset()
            when(index){
                0->{
                    pathSelect.addPath(pathFirst)
                }
                tabCount-1->{
                    pathSelect.addPath(pathLast)
                }
                else->{
                    val matrix=Matrix()
                    matrix.setTranslate(index*width.toFloat()/tabCount,0f)
                    pathCenter.transform(matrix,pathSelect)//我们把那个pathCenter 平移一下赋值给pathSelect即可
                }
            }
            pathOther.reset()
            pathOther.addPath(pathTotal)
            pathOther.fillType=Path.FillType.EVEN_ODD
            pathOther.addPath(pathSelect)
        }
    

    2个path都有了,设置背景即可
    这里用的setBackgroud的原因是为了保留ripple效果,最早我是直接写在onDraw里的,可那样的话ripple效果就被挡住了,看不见了【完全没弄明白为啥挡住了,按理parent的onDraw是画在最底层的,咋能挡住里边child的ripple效果?】

        private fun updateBg(){
            if(height>0&&width>0&&tabCount>0){
                val bitmap=Bitmap.createBitmap(width,height,Bitmap.Config.RGB_565)
                val canvas=Canvas(bitmap)
                checkSelectPath()
                p.setColor(Color.parseColor("#FF5722"))
                canvas.drawPath(pathSelect,p)
    
                p.setColor(Color.parseColor("#E91E63"))
                canvas.drawPath(pathOther,p)
    
                background=BitmapDrawable(resources,bitmap)
            }
        }
    

    监听tabListener

        override fun onTabSelected(tab: Tab?) {
            println("tab selected=======${tab?.position}")
            index=tab?.position?:0
            updateBg()
        }
    

    最后附上完整的代码

        <com.mitac.app2020.august.motion.CustomBGTabLayout
            android:id="@+id/custom"
            android:layout_marginTop="20dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/toolbar"
            android:layout_width="match_parent"
            app:tabGravity="fill"
            app:tabMode="fixed"
            app:tabTextColor="#fff"
            app:tabSelectedTextColor="@color/colorPrimary"
            app:tabMaxWidth="2000dp"
            android:layout_height="70dp"/>
    
    package com.mitac.app2020.august.motion
    
    import android.content.Context
    import android.graphics.*
    import android.graphics.drawable.BitmapDrawable
    import android.util.AttributeSet
    import com.google.android.material.tabs.TabLayout
    
    class CustomBGTabLayout : TabLayout, TabLayout.OnTabSelectedListener {
        constructor(context: Context) : super(context)
        constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
        constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
            context,
            attrs,
            defStyleAttr
        )
    
        private val p = Paint()
    
        init {
            p.flags = Paint.ANTI_ALIAS_FLAG
            p.style = Paint.Style.FILL
    
        }
    
        private var index = 0
    
        var delet = 20f
        val pathTotal = Path()
        val pathFirst = Path()
        val pathLast = Path()
        val pathCenter = Path()
        val pathSelect = Path()
        val pathOther = Path()
        override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
            super.onLayout(changed, left, top, right, bottom)
            if (tabCount == 0) {
                return
            }
            delet = height / 4f
            val y = height.toFloat()
    
            pathTotal.reset()
            pathTotal.addRect(0f, 0f, width.toFloat(), y, Path.Direction.CW)
    
            var normalX0 = width * 1f / tabCount;
            pathFirst.reset()
            pathFirst.moveTo(0f, 0f)
            pathFirst.lineTo(normalX0 - delet, 0f)
            pathFirst.cubicTo(
                normalX0,
                0f,
                normalX0,
                height.toFloat(),
                normalX0 + delet,
                height.toFloat()
            )
            pathFirst.lineTo(0f, height.toFloat())
            pathFirst.close()
    
            val normalXN = width - normalX0
    
            pathLast.reset()
            pathLast.moveTo(normalXN + delet, 0f)
            pathLast.cubicTo(normalXN, 0f, normalXN, y, normalXN - delet, y)
            pathLast.lineTo(width.toFloat(), y)
            pathLast.lineTo(width.toFloat(), 0f)
            pathLast.close()
    
            //path center,temp location at (0,0)
            pathCenter.reset()
            pathCenter.moveTo(delet, 0f)
            pathCenter.cubicTo(0f, 0f, 0f, y, -delet, y)
            pathCenter.lineTo(normalX0 + delet, y)
            pathCenter.cubicTo(normalX0, y, normalX0, 0f, normalX0 - delet, 0f)
            pathCenter.close()
    
            updateBg()
        }
    
        private fun checkSelectPath() {
            pathSelect.reset()
            when (index) {
                0 -> {
                    pathSelect.addPath(pathFirst)
                }
                tabCount - 1 -> {
                    pathSelect.addPath(pathLast)
                }
                else -> {
                    val matrix = Matrix()
                    matrix.setTranslate(index * width.toFloat() / tabCount, 0f)
                    pathCenter.transform(matrix, pathSelect)
                }
            }
            pathOther.reset()
            pathOther.addPath(pathTotal)
            pathOther.fillType = Path.FillType.EVEN_ODD
            pathOther.addPath(pathSelect)
        }
    
        private fun updateBg() {
            if (height > 0 && width > 0 && tabCount > 0) {
                val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
                val canvas = Canvas(bitmap)
                checkSelectPath()
                p.setColor(Color.parseColor("#FF5722"))
                canvas.drawPath(pathSelect, p)
    
                p.setColor(Color.parseColor("#E91E63"))
                canvas.drawPath(pathOther, p)
    
                background = BitmapDrawable(resources, bitmap)
            }
        }
    
        override fun onAttachedToWindow() {
            super.onAttachedToWindow()
            addOnTabSelectedListener(this)
        }
    
        override fun onDetachedFromWindow() {
            super.onDetachedFromWindow()
            removeOnTabSelectedListener(this)
        }
    
        override fun onTabReselected(tab: Tab?) {
    
        }
    
        override fun onTabUnselected(tab: Tab?) {
    
        }
    
        override fun onTabSelected(tab: Tab?) {
            println("tab selected=======${tab?.position}")
            index = tab?.position ?: 0
            updateBg()
        }
    }
    

    相关文章

      网友评论

          本文标题:TabLayout背景

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