仿IOS标题切换控件

  • 再次更新
    使用中发现因为每个矩形都是左右两个小的拼成的,有时候中间会出现重叠的现象,也就是1px宽,考虑是使用RectF的原因,把RectF修改为Rect

  • 更新
    圆角矩形边框效果不好,像是被切了一般,原因是画笔有一定宽度,画到外面去了,就还剩一半,修改了一下
//画圆角矩形边框
        mPaint.reset()
        mPaint.color = textColor
        mPaint.style = Paint.Style.STROKE
        mPaint.strokeWidth = Utils.dp2px(context, 1f)
        mPaint.isAntiAlias = true
        rectF.left = rectF.left + mPaint.strokeWidth / 2
        rectF.top = rectF.top + mPaint.strokeWidth / 2
        rectF.right = rectF.right - mPaint.strokeWidth / 2
        rectF.bottom = rectF.bottom - mPaint.strokeWidth / 2
        canvas.drawRoundRect(rectF, corner, corner, mPaint)

仿IOS标题切换控件_第1张图片
修改之后的效果.png

每次都设计成ios原生控件的效果,android开发真是难。之前写过一次这个效果,在布局文件里写的,真的很麻烦。这次又有这个需求,写一个自定义view,以后再用就方便多了,记录一下。


仿IOS标题切换控件_第2张图片
ios.jpg

自定义view代码

class IOSTab(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : View(context, attrs, defStyleAttr) {

    private var mTabs: Array = arrayOf("")
    private var selectedPosition = 0
    private var mWidth = 0
    private var mHeight = 0
    private var corner = Utils.dp2px(context, 5f)
    private val mPaint = Paint()

    //字号
    private var textSize = Utils.dp2px(context,15)
    //主色调
    private var mainColor = context.resources.getColor(R.color.colorPrimary)
    //字的颜色
    private var textColor = context.resources.getColor(R.color.white)
    //选中的颜色
    private var selectColor = Color.parseColor("#ccffffff")

    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
    constructor(context: Context) : this(context, null)

    init {
        val typedArray = context.obtainStyledAttributes(attrs, R.styleable.IOSTab)
        mainColor = typedArray.getColor(R.styleable.IOSTab_tabMainColor,context.resources.getColor(R.color.colorPrimary))
        textColor = typedArray.getColor(R.styleable.IOSTab_tabTextColor,context.resources.getColor(R.color.white))
        selectColor = typedArray.getColor(R.styleable.IOSTab_tabSelectColor,Color.parseColor("#ccffffff"))
        textSize = typedArray.getDimension(R.styleable.IOSTab_tabTextSize,Utils.dp2px(context,15))
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        mWidth = MeasureSpec.getSize(widthMeasureSpec)
        mHeight = MeasureSpec.getSize(heightMeasureSpec)
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        val rectF = RectF(0f, 0f, mWidth.toFloat(), mHeight.toFloat())
        //画背景
        mPaint.isAntiAlias = true
        mPaint.color = mainColor
        mPaint.style = Paint.Style.FILL
        canvas!!.drawRoundRect(rectF, corner, corner, mPaint)
        //画圆角矩形边框
        mPaint.reset()
        mPaint.color = textColor
        mPaint.style = Paint.Style.STROKE
        mPaint.strokeWidth = Utils.dp2px(context, 1f)
        mPaint.isAntiAlias = true
        canvas!!.drawRoundRect(rectF, corner, corner, mPaint)

        //画每一个字
        mPaint.reset()
        mPaint.color = textColor
        mPaint.isAntiAlias = true
        mPaint.textSize = textSize

        val count = mTabs.size
        if (count == 0) {
            return
        } else {
            //画字
            for (i in mTabs.indices){
                drawText(i,canvas,mPaint)
            }
        }
        //每个item的宽度
        val itemWidth = mWidth / mTabs.size .toFloat()
        //画分隔线
        mPaint.strokeWidth = Utils.dp2px(context,0.5f)
        mTabs.indices
                .filter { it != 0 }
                .forEach {
                    canvas.drawLine(it * itemWidth,0f, it * itemWidth, mHeight.toFloat(),mPaint)
                }


        mPaint.reset()
        mPaint.color = selectColor
//        mPaint.alpha = 0xcc
        mPaint.style = Paint.Style.FILL

        //画选中状态
        if (count == 0 || count == 1){
            return
        }else{
            if (selectedPosition == 0){
                //第一个被选中,把左半边画上圆角
                canvas.save()
                //只画左半边
                canvas.clipRect(RectF(0f,0f,itemWidth /2,mHeight.toFloat()))
                //圆角矩形
                val rectF = RectF(0f,0f,itemWidth,mHeight.toFloat())
                canvas.drawRoundRect(rectF, corner, corner, mPaint)
                canvas.restore()

                canvas.drawRect(RectF(itemWidth /2,0f,itemWidth, mHeight.toFloat()),mPaint)
            }else if (selectedPosition == count -1){
                //最后一个被选中,右半边画上圆角
                canvas.save()
                //只画右半边
                canvas.clipRect(RectF((selectedPosition + 0.5f)* itemWidth,0f,count * itemWidth,mHeight.toFloat()))
                //圆角矩形
                val rectF = RectF(selectedPosition* itemWidth,0f,count* itemWidth,mHeight.toFloat())
                canvas.drawRoundRect(rectF, corner, corner, mPaint)
                canvas.restore()
                canvas.drawRect(RectF(selectedPosition* itemWidth,0f,(selectedPosition + 0.5f)* itemWidth, mHeight.toFloat()),mPaint)
            }else{
                //选中中间的
                val rect = Rect((itemWidth * selectedPosition).toInt()
                        , 0
                        , (itemWidth*(selectedPosition+1)).toInt()
                        , mHeight)
                canvas.drawRect(rect,mPaint)
            }

        }

        //画选中的条目的文字
        mPaint.reset()
        mPaint.color = mainColor
        mPaint.isAntiAlias = true
        mPaint.textSize = textSize
        drawText(selectedPosition,canvas,mPaint)

    }

    private fun drawText(position: Int, canvas: Canvas, paint: Paint) {
        val text = mTabs[position]
        val textWidth = paint.measureText(text)
        val itemWidth = mWidth / mTabs.size
        //X起始坐标,X轴居中
        val startX = (itemWidth - textWidth) / 2 + itemWidth * position
        //Y起始坐标,Y轴居中
        val startY = mHeight / 2 + (Math.abs(mPaint.fontMetrics.ascent) - mPaint.fontMetrics.descent) / 2

        canvas.drawText(text, startX, startY, paint)
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        if (event!!.action == MotionEvent.ACTION_DOWN){
            if (mTabs.isNotEmpty()){
                selectedPosition = (event.x / (mWidth / mTabs.size)).toInt()
                invalidate()
                if (listener != null)
                    listener!!.onChange(selectedPosition)
            }
        }
        return super.onTouchEvent(event)
    }

    private var  listener: IOSTab.OnSelectedItemChange? = null

    //设置切换监听
    fun setOnSelectedItemChange(listener: OnSelectedItemChange){
        this.listener = listener
    }

    fun setTabs(tabs: Array) {
        mTabs = tabs
        invalidate()
    }

    interface OnSelectedItemChange {
        fun onChange(select: Int)
    }
}

attrs文件中添加


        
        
        
        
        
        
        
        
    

使用

在布局文件中


在activity或者fragment中

val tabs = arrayOf("标题一","标题二","标题三")
ios_tab.setTabs(tabs)
ios_tab.setOnSelectedItemChange(object : IOSTab.OnSelectedItemChange{
            override fun onChange(select: Int) {
                //在这里处理切换
            }
        })

效果


仿IOS标题切换控件_第3张图片
效果.png

你可能感兴趣的:(仿IOS标题切换控件)