2019-12-23 自定义水波纹View

一、Path相关方法的运用

  • moveTo :移到下一次操作的起点位置

  • lineTo :添加上一个点到当前点之间的直线到Path

  • Close:连接第一个点连接到最后一个点,形成一个闭合区域

  • quadTo,cubicTo : (quadTo(x1,y1,x2,y2)中(x1,y1)为控制点的坐标,(x2,y2)为终点的坐标。分别为二次和三次贝塞尔曲线的方法

二、对于Path的运用(贝塞尔曲线)

  • 贝塞尔曲线由一系列点来控制曲线状态的,这些点分为两类:数据点,控制点

  • 一阶贝塞尔曲线:仅有数据点(A和B),是一条直线

  • 二阶贝塞尔曲线:有两个数据点,一个控制点来描述的曲线

    2019-12-23 自定义水波纹View_第1张图片
    image-20190308141902907.png
  • 三阶贝塞尔曲线:两个数据点,两个控制点
2019-12-23 自定义水波纹View_第2张图片
image-20190308141943108.png

三、绘制水波纹View


class WaveView : View{

    constructor(context: Context) : super(context) {
        initAttrs(context, null)
        initPaintAndPath()
    }

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        initAttrs(context, attrs)
        initPaintAndPath()
    }

    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
        initAttrs(context, attrs)
        initPaintAndPath()
    }


    private lateinit var headPaint: Paint
    private lateinit var footPaint: Paint
    private lateinit var borderPaint: Paint

    private lateinit var headerPath: Path
    private lateinit var footPath: Path
    private lateinit var circlePath: Path // 主要用于剪切

    /**
     * 里波长
     */
    private var headWaveWidth: Float = 600f

    /**
     * 里波高度
     */
    private var headWaveHeight: Float = 45f

    /**
     * 里波的颜色
     */
    private var headPaintColor: Int = Color.parseColor("#ff0000")


    /**
     * 外波长
     */
    private var footWaveWidth: Float = 550f

    /**
     * 外波高度
     */
    private var footWaveHeight: Float = 35f

    /**
     * 外波颜色
     */
    private var footPaintColor: Int = Color.parseColor("#50ff0000")

    private var pxWidth: Float = 0f
    private var pxHeight: Float = 0f

    /**
     * 外围圆的线的宽度
     */
    private var borderWidth: Float = 10f

    /**
     * 外围圆的线的颜色
     */
    private var borderColor: Int = Color.parseColor("#ffffff")

    /**
     * 默认第一次重绘时里面波移动的距离
     */
    private var headOffset = 12f

    /**
     * 默认第一次重绘时外面波移动的距离
     */
    private var footOffset = 4f

    /**
     * 波的目前高度
     */
    private var mChangeY: Float = 0f

    /**
     * 波的数量
     */
    private var waveCount: Int = 0

    /**
     * 水位最终高度
     */
    private var finalY: Float = 0f

    /**
     * 重绘值波峰移动距离的和
     */
    private var moveSumHead: Float = 0f
    private var moveSumFoot: Float = 0f

    private var percent: Float = 0f //需要的最终百分比
    private var currentPercent: Float = 0f //当前的百分比

    private var timeOffset: Long = 0 // 重绘间隔时间
    private var drawFlag = false


    private fun initAttrs(mContext: Context, attrs: AttributeSet?) {
        if(attrs == null) return
        val typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.WaveView)
        borderWidth = typedArray.getDimension(R.styleable.WaveView_borderWidth, 10f)

        headPaintColor = typedArray.getColor(R.styleable.WaveView_headColor, Color.parseColor("#ff0000"))
        footPaintColor = typedArray.getColor(R.styleable.WaveView_footColor, Color.parseColor("#50ff0000"))
        borderColor = typedArray.getColor(R.styleable.WaveView_borderColor, Color.parseColor("#ffffff"))

        headWaveWidth = typedArray.getDimension(R.styleable.WaveView_headWaveWidth, 600f)
        headWaveHeight = typedArray.getDimension(R.styleable.WaveView_headWaveHeight, 45f)

        footWaveWidth = typedArray.getDimension(R.styleable.WaveView_footWaveWidth, 550f)
        footWaveHeight = typedArray.getDimension(R.styleable.WaveView_footWaveHeight, 35f)

        headOffset = typedArray.getDimension(R.styleable.WaveView_headOffset,12f)
        footOffset = typedArray.getDimension(R.styleable.WaveView_footOffset,4f)

        timeOffset = typedArray.getFloat(R.styleable.WaveView_timeOffset,2f).toLong()

        typedArray.recycle()
    }

    private fun initPaintAndPath() {
        headPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        headPaint.color = headPaintColor
        headPaint.style = Paint.Style.FILL

        footPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        footPaint.color = footPaintColor
        footPaint.style = Paint.Style.FILL

        borderPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        borderPaint.color = borderColor
        borderPaint.strokeWidth = borderWidth
        borderPaint.style = Paint.Style.STROKE

        headerPath = Path()
        footPath = Path()
        circlePath = Path()

    }

    override fun onDraw(canvas: Canvas?) {
        Log.d("yxy", "onDraw $headWaveWidth" )
        //画边界圆
        drawBorder(canvas ?: return)

        //画波浪
        drawWave(canvas ?: return)

    }

    fun setPercent(percent: Float) {
        this.percent = percent
        //根据百分比设置最终水位
        finalY = (1 - percent) * pxHeight
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        pxWidth = measuredWidth.toFloat()
        pxHeight = measuredHeight.toFloat()

        mChangeY = Math.min(pxWidth, pxHeight)
        //计算波峰个数
        waveCount = Math.round(pxWidth / headWaveWidth + 1.5f) //保证至少2个波纹
    }

    /**
     * 需要圆形以外的不显示,所以需要用到剪切功能
     */
    private fun drawBorder(canvas: Canvas) {
        circlePath.reset()

        val radius = (pxWidth - 2 * borderWidth) / 2
        val centerX = radius + borderWidth
        val centerY = radius + borderWidth

        // 使用canvas的方法剪切一个圆形显示,其余地方不显示
        circlePath.addCircle(centerX, centerY, radius, Path.Direction.CW)
        canvas.clipPath(circlePath)
        canvas.drawCircle(centerX, centerY, radius, borderPaint)

    }

    private fun drawWave(canvas: Canvas?) {
        headerPath.reset()
        footPath.reset()

        headerPath.moveTo(-headWaveWidth, mChangeY)
        footPath.moveTo(-footWaveWidth, mChangeY)

        for (i in 0..waveCount) {
            //headWaveView路径 
            headerPath.quadTo(
                    (-headWaveWidth * 3 / 4) + i * headWaveWidth + moveSumHead,
                    mChangeY - headWaveHeight,
                    (-headWaveWidth / 2) + i * headWaveWidth + moveSumHead,
                    mChangeY
            )

            headerPath.quadTo(
                    (-headWaveWidth * 1 / 4) + i * headWaveWidth + moveSumHead,
                    mChangeY + headWaveHeight,
                    i * headWaveWidth + moveSumHead,
                    mChangeY
            )

            //footWave路径
            footPath.quadTo(
                    (-footWaveWidth * 3 / 4) + i * footWaveWidth + moveSumFoot,
                    mChangeY - footWaveHeight,
                    (-footWaveWidth / 2) + i * footWaveWidth + moveSumFoot,
                    mChangeY
            )

            footPath.quadTo(
                    (-footWaveWidth * 1 / 4) + i * footWaveWidth + moveSumFoot,
                    mChangeY + footWaveHeight,
                    i * footWaveWidth + moveSumFoot,
                    mChangeY
            )

        }
        //不断改变高度,实现逐渐水位上涨效果
        mChangeY -= 1
        if (mChangeY < finalY) mChangeY = finalY
        moveSumHead += headOffset
        moveSumFoot -= footOffset

        if (moveSumHead >= headWaveWidth) moveSumHead = 0F
        if (moveSumFoot <= 0) moveSumFoot = footWaveWidth

        headerPath.lineTo(pxWidth, pxHeight)
        headerPath.lineTo(0f, pxHeight)
        headerPath.close()

        footPath.lineTo(pxWidth, pxHeight)
        footPath.lineTo(0f, pxHeight)
        footPath.close()

        canvas?.drawPath(headerPath, headPaint)
        canvas?.drawPath(footPath, footPaint)
        currentPercent = 1 - mChangeY / pxHeight
        Log.d("yxy", "percent = $percent,currentPercent = $currentPercent")
        if(drawFlag)postInvalidateDelayed(timeOffset)
    }

    fun start(){
        drawFlag = true
        postInvalidate()
    }

}


//style
 
        
        
        
        
        
        
        
        
        
        
        
    
    
 //布局文件:
         

你可能感兴趣的:(2019-12-23 自定义水波纹View)