安卓自定义view实现右上角红点及数量展示(此控件再列表view中可能会导致红点横向坐标错位)

1,009 阅读3分钟

kotlin代码编写

  1. 可以设置只展示红点
  2. 数量为0时隐藏此view
  3. 数量为0-9时是原形红点
  4. 数量大于9 小于等于99时展示具体数量
  5. 数量为大于99时显示···
  6. 可以自己设置外圈圆环,及其颜色。

view代码

package com.k.android.view

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import com.k.android.R
import com.k.android.view.calendar.Utils

class RedDotView(context: Context, attr: AttributeSet) : View(context,attr) {
    private val threeDot:String="···"
    private var attrBean = DotViewAttr(context,attr)
    private var mOnlyDot:Boolean=false
    private var mText:Int=0
    private var paint = Paint()//画笔
    private val rect= Rect()//计算文字宽高的矩形

    private var textWidth  = 0
    private var textHeight = 0

    private var viewWidth = 0
    private var viewHeight = 0

    private val ringWidth = 4
    override fun draw(canvas: Canvas?) {
        super.draw(canvas)
        paint.isAntiAlias=true//设置抗锯齿
        if(mOnlyDot){//只画一个圆点   半径的一半为中心点
            paint.color = attrBean.dotColor
            if(attrBean.dotShowRing){
                paint.color=attrBean.dotRingColor
                canvas?.drawCircle(viewWidth.toFloat()/2,viewHeight.toFloat()/2,attrBean.dotDiameter.toFloat()/2,paint)
                paint.color=attrBean.dotColor
                canvas?.drawCircle(viewWidth.toFloat()/2,viewHeight.toFloat()/2,attrBean.dotDiameter.toFloat()/2-ringWidth,paint)
            }else{
                paint.color=attrBean.dotColor
                canvas?.drawCircle(viewWidth.toFloat()/2,viewHeight.toFloat()/2,attrBean.dotDiameter.toFloat()/2,paint)
            }
        }else{
            if(mText==0){//等于0不显示此view所以不执行任何代码
                visibility = GONE
                return
            }
            if(mText in 1..9){//画圆
                //画圆
                if(attrBean.dotShowRing){
                    paint.color=attrBean.dotRingColor
                    canvas?.drawCircle(viewWidth.toFloat()/2,viewHeight.toFloat()/2,viewHeight.toFloat()/2,paint)
                    paint.color=attrBean.dotColor
                    canvas?.drawCircle(viewWidth.toFloat()/2,viewHeight.toFloat()/2,viewHeight.toFloat()/2-ringWidth,paint)
                }else{
                    paint.color=attrBean.dotColor
                    canvas?.drawCircle(viewWidth.toFloat()/2,viewHeight.toFloat()/2,viewHeight.toFloat()/2,paint)
                }
                //写字
                paint.color = attrBean.dotTextColor
                paint.textSize = attrBean.dotTextSize.toFloat()
                canvas?.drawText(mText.toString(),(viewWidth-textWidth).toFloat()/2-1,(viewHeight+textHeight).toFloat()/2-1,paint)
            }else{//画圆角矩形
                //计算文字宽高
                //画圆角矩形
                if(attrBean.dotShowRing){
                    paint.color = attrBean.dotRingColor
                    val oval = RectF(0f, 0f, viewWidth.toFloat(), viewHeight.toFloat())// 设置个新的长方形
                    canvas?.drawRoundRect(oval, viewHeight.toFloat()/2, viewHeight.toFloat()/2, paint)//第二个参数是x半径,第三个参数是y半径
                    paint.color = attrBean.dotColor
                    val oval2 = RectF(ringWidth.toFloat(), ringWidth.toFloat(), viewWidth.toFloat()-ringWidth, viewHeight.toFloat()-ringWidth)// 设置个新的长方形
                    canvas?.drawRoundRect(oval2, viewHeight.toFloat()/2-ringWidth, viewHeight.toFloat()/2-ringWidth, paint)//第二个参数是x半径,第三个参数是y半径
                }else{
                    paint.color = attrBean.dotColor
                    val oval = RectF(0f, 0f, viewWidth.toFloat(), viewHeight.toFloat())// 设置个新的长方形
                    canvas?.drawRoundRect(oval, viewHeight.toFloat()/2, viewHeight.toFloat()/2, paint)//第二个参数是x半径,第三个参数是y半径
                }
                //写字
                paint.color = attrBean.dotTextColor
                paint.textSize = attrBean.dotTextSize.toFloat()
                if(mText>99){
                    canvas?.drawText(threeDot,(viewWidth-textWidth).toFloat()/2-5,(viewHeight+textHeight).toFloat()/2+6,paint)
                }else{
                    canvas?.drawText(mText.toString(),(viewWidth-textWidth).toFloat()/2-1,(viewHeight+textHeight).toFloat()/2-1,paint)
                }
            }
        }
    }

    fun setText(text:Int){
        if (text==0){
            visibility = GONE
        }else{
            visibility = VISIBLE
        }
        mText = text
        initWidthAHeight()
        setOnlyDotMode(false)//设置数量时让只是红点模式为false
        invalidate()
    }

    private fun initWidthAHeight(){
        paint.textSize = attrBean.dotTextSize.toFloat()
        if(mText>99){
            paint.getTextBounds(threeDot,0,threeDot.length,rect)
        }else{
            paint.getTextBounds(mText.toString(),0,"$mText".length,rect)
        }
        textWidth = rect.width()
        textHeight = rect.height()
        if(mText >9){
            viewWidth = attrBean.dotHeight+textWidth
        }else if(mText in 1..9){
            viewWidth = attrBean.dotHeight
        }
        viewHeight = attrBean.dotHeight
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        initParams()
    }

    fun setOnlyDotMode(onlyDot:Boolean){
        mOnlyDot = onlyDot
        initParams()
        invalidate()
    }

    /**
     * 默认,
     */
    fun setOffset(view:View){
        view.viewTreeObserver.addOnGlobalLayoutListener {
            var x=0
            var y=0
            if (mOnlyDot){
                x=Utils.dpi2px(context,-5f)
                y=Utils.dpi2px(context,5f)
            }else{
                x=Utils.dpi2px(context,-6f)
                y=Utils.dpi2px(context,7f)
            }
            setX(view.right.toFloat()-viewWidth/2+x)
            setY(view.top.toFloat()-viewHeight/2+y)
        }
    }

    //特殊定制,首页底部消息显示红点需要
    fun setOffset(view:View,x:Int,y:Int,x1:Int,y1:Int){
        view.viewTreeObserver.addOnGlobalLayoutListener {
            var w=0
            var h=0
            if (mOnlyDot){
                w=Utils.dpi2px(context,x.toFloat())
                h=Utils.dpi2px(context,y.toFloat())
            }else{
                w=Utils.dpi2px(context,x1.toFloat())
                h=Utils.dpi2px(context,y1.toFloat())
            }
            setX(view.right.toFloat()-viewWidth/2+w)
            setY(view.top.toFloat()-viewHeight/2+h)
        }
    }

    private fun initParams(){
        if(mOnlyDot){
            visibility = VISIBLE
            layoutParams.width = attrBean.dotDiameter
            layoutParams.height = attrBean.dotDiameter
            viewHeight = attrBean.dotDiameter
            viewWidth = attrBean.dotDiameter
        }else{
            if(mText >9){
                layoutParams.width = attrBean.dotHeight+textWidth
                layoutParams.height = attrBean.dotHeight
            }else{
                layoutParams.width = attrBean.dotHeight
                layoutParams.height = attrBean.dotHeight
            }
        }
        requestLayout()
    }
}
class DotViewAttr(context:Context,attrs:AttributeSet){
    val dotColor: Int //绘制的圆圈颜色
    val dotTextSize: Int //绘制的文本颜色
    val dotHeight: Int //绘制的圆环view高度 ,因为宽度是自适应的,所以不需要写
    var dotDiameter:Int=0 //当只显示小红点时绘制的点的直径
    val dotTextColor: Int //绘制的文本的颜色
    val dotShowRing:Boolean //当需要外部显示边时,设置此项为true
    val dotRingColor:Int//要显示边的颜色
    init {
        val ta = context.obtainStyledAttributes(attrs, R.styleable.RedDotView)
        dotColor = ta.getColor(R.styleable.RedDotView_dotColor,Color.RED)
        dotTextSize = ta.getDimensionPixelSize(R.styleable.RedDotView_dotTextSize, 15)
        dotDiameter = ta.getDimensionPixelSize(R.styleable.RedDotView_dotDiameter, 10)
        dotHeight = ta.getDimensionPixelSize(R.styleable.RedDotView_dotHeight, 10)
        dotTextColor = ta.getColor(R.styleable.RedDotView_dotTextColor, Color.WHITE)
        dotRingColor = ta.getColor(R.styleable.RedDotView_dotRingColor, Color.WHITE)
        dotShowRing = ta.getBoolean(R.styleable.RedDotView_dotShowRing,false)
        ta.recycle()
    }
}

属性代码

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="RedDotView">
        <attr name="dotColor" format="color"/>
        <attr name="dotWidth" format="dimension"/>
        <attr name="dotHeight" format="dimension"/>
        <attr name="dotMiniWidth" format="dimension"/>
        <attr name="dotShowRing" format="boolean"/>
        <attr name="dotTextSize"/>
        <attr name="dotDiameter" format="dimension"/>
        <attr name="dotTextColor" format="color"/>
        <attr name="dotRingColor" format="color"/>
    </declare-styleable>
</resources>

显示效果

  1. 单一红点的情况,中心距离图片右上角 向左5dp,向下5dp(setOnlyDotMode(true))

  1. 底部双位数情况,红色中心距离图片右上角自定义,并且红色区域外围有自定义颜色边框

  1. 显示单位数情况无边框

  2. 超过100显示...的情况

完!