安卓自定义View实例--矩形电量View

UI原型图:

 

 

 

实现效果:

 

安卓自定义View实例--矩形电量View_第1张图片

 

 

 

 

Gif 图有点失真

功能:

1:进度条颜色和底部背景色支持自定义颜色,支持渐变色

2:数字和百分比符号支持分别设置大小和颜色

3:文字默认居中显示,支持底部显示

用法:

1:在布局文件中引入

2:在代码中调用setProgress()方法。

//设置进度
chargeProgressBar.setProgress(mProgress,true)
//设置文字位置
chargeProgressBar.setTextGravity(ChargeProgressBar.GRAVITY.GRAVITY_CENTER)

 

代码实现:

 
        
        
        
        
        
        
        
        
            
            
            
        
        
        

    
package com.lzk.customviewpractice.widget

import android.animation.ObjectAnimator
import android.content.Context
import android.graphics.*
import android.os.Build
import android.util.AttributeSet
import android.util.TypedValue
import android.view.View
import com.lzk.customviewpractice.R
import kotlin.math.max
import kotlin.math.min


/**
 * Author: LiaoZhongKai
 * Date: 2020/5/25 11:27
 * Description:电量显示进度条
 */
class ChargeProgressBar : View {

    //bar
    private var mReachBarPrimaryColor: Int = Color.parseColor("#03C9A6")
    private var mReachBarSecondColor: Int = Color.parseColor("#00FBC8")
    private var mUnReachBarPrimaryColor: Int = Color.parseColor("#003647")
    private var mUnReachBarSecondColor: Int = Color.parseColor("#003647")
    private var mCorner: Float = dp2px(1f)//dp
    //文字
    private var mTextColor: Int = Color.WHITE
    private var mTextSize: Float = sp2px(10f)//sp
    private var mTextGravity: Int = GRAVITY.GRAVITY_CENTER.value
    private var mPercentageSize: Float = sp2px(8f)//sp
    private var mPercentageColor: Int = Color.WHITE
    private val mPercentageText = "%"
    //去掉padding的真正宽度
    private var mRealWidth: Float = 0f
    private var mRealHeight: Float = 0f
    //paint
    private var mPaint = Paint()
    private var mPercentagePaint = Paint()
    //渐变色shader
    private lateinit var mReachBarShader: Shader
    private lateinit var mUnReachBarShader: Shader
    private var mPath: Path = Path()
    private var mMatrix: Matrix = Matrix()
    //左右顶点偏离距离
    private var mOffset: Float = 0f

    private var mProgress: Int = 0
    private val mMaxProgress: Int = 100

    private var mAnimator: ObjectAnimator? = null
    private var mIsAnim: Boolean = true
    //存储当前进度
    private var mTempProgress: Int = 0

    constructor(context: Context?) : this(context,null)
    constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs,0)
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    ){
        obtainAttrs(attrs)
    }

    //获取自定义属性
    private fun obtainAttrs(attrs: AttributeSet?){
        val typeArray =  context.obtainStyledAttributes(attrs,R.styleable.ChargeProgressBar)
        mReachBarPrimaryColor = typeArray.getColor(R.styleable.ChargeProgressBar_reach_bar_primary_color,mReachBarPrimaryColor)
        mReachBarSecondColor = typeArray.getColor(R.styleable.ChargeProgressBar_reach_bar_second_color,mReachBarSecondColor)
        mUnReachBarPrimaryColor = typeArray.getColor(R.styleable.ChargeProgressBar_unreach_bar_primary_color,mUnReachBarPrimaryColor)
        mUnReachBarPrimaryColor = typeArray.getColor(R.styleable.ChargeProgressBar_unreach_bar_second_color,mUnReachBarSecondColor)
        mCorner = typeArray.getDimension(R.styleable.ChargeProgressBar_corner,dp2px(mCorner))
        mTextColor = typeArray.getColor(R.styleable.ChargeProgressBar_text_color,mTextColor)
        mTextSize = typeArray.getDimension(R.styleable.ChargeProgressBar_text_size,sp2px(mTextSize))
        mTextGravity = typeArray.getInt(R.styleable.ChargeProgressBar_text_gravity, GRAVITY.GRAVITY_CENTER.value)
        mPercentageColor = typeArray.getColor(R.styleable.ChargeProgressBar_percentage_text_color,mPercentageColor)
        mPercentageSize = typeArray.getDimension(R.styleable.ChargeProgressBar_percentage_text_size,mPercentageSize)
        typeArray.recycle()

        mPaint.textSize = mTextSize
        mPercentagePaint.textSize = mPercentageSize
        mPaint.isAntiAlias = true
        mPercentagePaint.isAntiAlias = true

    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        var widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        var heightSize = MeasureSpec.getSize(heightMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)

        val textHeight = mPaint.descent() - mPaint.ascent()
        val percentageHeight = mPercentagePaint.descent() - mPercentagePaint.ascent()

        val width = dp2px(150f)//dp
        if (widthMode == MeasureSpec.AT_MOST){
            widthSize =min(widthSize,width.toInt())+paddingStart+paddingEnd
        }
        val height = dp2px(16f)//dp
        if (heightMode == MeasureSpec.AT_MOST){
            val minHeight = min(height.toInt(),heightSize)+paddingTop+paddingBottom
            val maxTextHeight = max(textHeight,percentageHeight)
            heightSize = max(maxTextHeight,minHeight.toFloat()).toInt()
        }
        setMeasuredDimension(widthSize,heightSize)

        mRealWidth = (measuredWidth - paddingStart - paddingEnd).toFloat()
        mRealHeight = (measuredHeight - paddingBottom - paddingTop).toFloat()
        mOffset = (mRealWidth*0.1f)/2

        mReachBarShader = LinearGradient(0f,0f,0f,mRealHeight,
            intArrayOf(mReachBarPrimaryColor,mReachBarSecondColor),null,Shader.TileMode.CLAMP)
        mUnReachBarShader = LinearGradient(0f,0f,0f,mRealHeight,
            intArrayOf(mUnReachBarPrimaryColor,mUnReachBarSecondColor),null,Shader.TileMode.CLAMP)
    }

    override fun onDraw(canvas: Canvas) {
        canvas.save()
        //平移坐标轴
        canvas.translate(paddingStart.toFloat(),paddingTop.toFloat())
        var ratio = mProgress.toFloat()/mMaxProgress.toFloat()
        //比例超过1,按百分之百处理
        if (ratio > 1) ratio = 1f
        val progressX = ratio*mRealWidth
        //画背景
        mPaint.apply {
            shader = mUnReachBarShader
            style = Paint.Style.FILL
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                mPath.reset()
                mPath.addRoundRect(0f,0f,mRealWidth,mRealHeight,
                    floatArrayOf(mCorner,
                        mCorner,
                        mCorner,mCorner,
                        mCorner,mCorner,
                        mCorner,
                        mCorner),
                    Path.Direction.CCW)
                //拉伸
                val srcArray = floatArrayOf(0f,0f,mRealWidth,0f,0f,mRealHeight,mRealWidth,mRealHeight)
                val dstArray = floatArrayOf(mOffset,0f,mRealWidth-mOffset,0f,0f,mRealHeight,mRealWidth,mRealHeight)
                mMatrix.reset()
                mMatrix.setPolyToPoly(srcArray,0,dstArray,0,4)
                canvas.concat(mMatrix)
                canvas.drawPath(mPath,this)
            }else{
                canvas.drawRect(progressX,0f,mRealWidth,mRealHeight,mPaint)
            }
        }

        //画进度条
        if (progressX > 0){
            mPaint.apply {
                shader = mReachBarShader
                style = Paint.Style.FILL
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    mPath.reset()
                    mPath.addRoundRect(0f,0f,progressX,mRealHeight,
                        floatArrayOf(
                            mCorner,
                            mCorner,
                            if (mProgress valueTextBaseLineY
            //底部
            GRAVITY.GRAVITY_BOTTOM.value -> mRealHeight - valueTextBaseLineY
            //居中
            else ->  mRealHeight/2f+valueTextBaseLineY
        }
        val valueTextWidth = mPaint.measureText(valueText)
        val percentageTextWidth = mPaint.measureText(mPercentageText)
        val valueTextX = mRealWidth/2f - (valueTextWidth+percentageTextWidth)/2

        //百分比符号
        mPercentagePaint.color = mPercentageColor
        mPercentagePaint.textSize = mPercentageSize
        mPercentagePaint.isFakeBoldText = true
        val percentageFontMetrics = mPercentagePaint.fontMetrics
        val percentageTextBaseLineY = (percentageFontMetrics.descent - percentageFontMetrics.ascent)/2 - percentageFontMetrics.descent
        val percentageTextX = valueTextX+valueTextWidth
//为什么用valueTextY - valueTextBaseLineY就能得到数字文字的中心点?
//valueTextY相当于数字文字baseline线的Y坐标,而valueTextBaseLineY相当于baseline线距离文字中线的距离,用baseline的Y坐标减去baseline线的高度就等于文件中线的Y坐标
        val percentageTextY = (valueTextY - valueTextBaseLineY)+percentageTextBaseLineY
        canvas.save()
        canvas.translate(paddingStart.toFloat(),paddingTop.toFloat())
        canvas.drawText(valueText,valueTextX,valueTextY,mPaint)
        canvas.drawText(mPercentageText,percentageTextX,percentageTextY,mPercentagePaint)
        canvas.restore()
    }

    private fun sp2px(spVal: Float): Float{
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,spVal,resources.displayMetrics)
    }

    private fun dp2px(dpVal: Float): Float{
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dpVal,resources.displayMetrics)
    }

    enum class GRAVITY(val value: Int){
        GRAVITY_TOP(1),
        GRAVITY_BOTTOM(2),
        GRAVITY_CENTER(3)
    }

    /**
     * 设置文字的位置
     */
    fun setTextGravity(gravity: GRAVITY){
        mTextGravity = gravity.value
        invalidate()
    }

    fun setProgress(progress: Int, anim: Boolean = true){
        mIsAnim = anim
        mTempProgress = progress
        if (mIsAnim){
            if (mAnimator != null && mAnimator!!.isRunning){
                mAnimator!!.cancel()
            }
            mAnimator = ObjectAnimator.ofInt(this,"progress",this.mProgress,progress).setDuration(500)
            mAnimator?.start()
        }else{
            this.mProgress = progress
            mTempProgress = progress
            invalidate()
        }
    }

    private fun setProgress(progress: Int) {
        this.mProgress = progress
        invalidate()
    }

    fun getProgress(): Int = mTempProgress

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        if (mAnimator != null && mAnimator!!.isRunning){
            mAnimator!!.cancel()
        }
    }
}

 

你可能感兴趣的:(安卓)