美文网首页
自定义View之HProgressView(水平进度条view)

自定义View之HProgressView(水平进度条view)

作者: TangBuzhi | 来源:发表于2018-03-26 18:08 被阅读0次
写在前面的话

话说kotlin被google确立正统地位(Android开发官方语言)已经快一年了,之前也断断续续学习过kotlin,但是由于实际项目开发中并未使用kotlin,因此kotlin并不熟悉。由于最近偶得闲暇,于是重拾kotlin,并计划使用kotlin重构之前写过的app,以此作为自己一段成长记录。
本文是kotlin实现的水平方向带进度条(支持动画)的自定义view。

1.效果图

不知不觉又扯远了,好了,回归正题,直接来看效果图:


h_progress_view.gif

2.实现思路

  • 绘制默认进度条(默认进度条两端是半圆,中间是矩形)
  • 绘制进度条经过的区域(左边是半圆,右边矩形,当进度条为100%时,最右的半圆也要绘制)
  • 绘制进度条下方的说明文字以及实际进度数值

3.细节阐述

3.1概述

该View舍弃继承ProgressBar,而继承自View,实现高度的可定制,尤其是该进度条两端是一些花哨的几何图形时,该View两端暂定为半圆(当然,如果两端图形不计较是啥时,继承ProgressBar会更轻松),首先重写View的三个构造方法,并初始化必要参数,接着获取该View宽高以及必要计算参数,然后就是绘制该View了,最后重写触摸事件方法,让该View响应点击以及滑动事件。

3.2

通过res/values/attrs定义如下细节参数

   <declare-styleable name="HProgressView">
        <attr name="progress" format="float" />
        <!--水平方向边距-->
        <attr name="h_margin" format="dimension" />
        <attr name="progress_color" format="color" />
        <attr name="normal_color" format="color" />
        <attr name="text_des_color" format="color" />
        <attr name="text_num_color" format="color" />
        <attr name="text_size" format="dimension" />
        <!--进度条的纵向宽度-->
        <attr name="progress_v_width" format="dimension" />
        <attr name="text_to_progress_margin" format="dimension" />
        <attr name="with_anim" format="boolean" />
        <attr name="with_click" format="boolean" />
    </declare-styleable>

3.3在构造方法中初始化attrs参数以及画笔paint

    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) {
        defProgressColor = ContextCompat.getColor(context, R.color.progress_color)
        defNormalColor = ContextCompat.getColor(context, R.color.progress_normal_color)
        defTxtDesColor = ContextCompat.getColor(context, R.color.progress_txt_des_color)
        defTxtNumColor = ContextCompat.getColor(context, R.color.progress_txt_num_color)
        defTxtSize = dip2px(16f)
        defProgressVWidth = dip2px(20f)
        defTxt2ProgressMargin = dip2px(30f)
        defHMargin = dip2px(10f)

        val a = context.obtainStyledAttributes(attrs, R.styleable.HProgressView)
        try {
            mProgress = a.getFloat(R.styleable.HProgressView_progress, 0f)
            hMargin = a.getDimension(R.styleable.HProgressView_h_margin, defHMargin)
            progressColor = a.getColor(R.styleable.HProgressView_progress_color, defProgressColor)
            normalColor = a.getColor(R.styleable.HProgressView_normal_color, defNormalColor)
            txtDesColor = a.getColor(R.styleable.HProgressView_text_des_color, defTxtDesColor)
            txtNumColor = a.getColor(R.styleable.HProgressView_text_num_color, defTxtNumColor)
            txtSize = a.getDimension(R.styleable.HProgressView_text_size, defTxtSize)
            progressVWidth = a.getDimension(R.styleable.HProgressView_progress_v_width, defProgressVWidth)
            txt2ProgressMargin = a.getDimension(R.styleable.HProgressView_text_to_progress_margin, defTxt2ProgressMargin)
            withAnim = a.getBoolean(R.styleable.HProgressView_with_anim, true)
        } finally {
            a.recycle()
        }

        if (mProgress < 0) mProgress = 0f
        if (mProgress > 100) mProgress = 100f

        normalPaint.color = normalColor
        normalPaint.style = Paint.Style.FILL

        progressPaint.color = progressColor
        progressPaint.style = Paint.Style.FILL

        txtPaint.style = Paint.Style.FILL
        txtPaint.textSize = txtSize

        bounds = Rect()
    }

3.4在onSizeChanged方法中,获取该View宽高以及必要计算参数

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        mWidth = w
        mHeight = h
        cirlceRadius = progressVWidth / 2
        startCenterX = cirlceRadius + hMargin
        endCenterX = mWidth - cirlceRadius - hMargin
        startCenterY = mHeight / 2 - txt2ProgressMargin / 2
        endCenterY = startCenterY

        top = startCenterY - cirlceRadius
        bottom = startCenterY + cirlceRadius
    }

3.5绘制View

3.5.1普通进度条
    private fun drawNormal(canvas: Canvas) {
        recLeftCircle = RectF(hMargin, top, hMargin + progressVWidth, bottom)
        canvas.drawArc(recLeftCircle, 90f, 180f, true, normalPaint)

        recRightCircle = RectF(mWidth - progressVWidth - hMargin, top, mWidth - hMargin, bottom)
        canvas.drawArc(recRightCircle, -90f, 180f, true, normalPaint)

        rectProgressArea = RectF(hMargin + cirlceRadius, top, mWidth - hMargin - cirlceRadius, bottom)
        canvas.drawRect(rectProgressArea, normalPaint)
    }
3.5.2完成进度条
        private fun drawProgress(canvas: Canvas) {
        if (mProgress > 0) {
            canvas.drawArc(recLeftCircle, 90f, 180f, true, progressPaint)

            if (mProgress >= 0.5f) {//为了防止mprogress为0.1-0.5时左边绘制有误
                val left = hMargin + cirlceRadius
                val right = (mWidth - 2 * hMargin - progressVWidth) * mProgress / 100 + hMargin + cirlceRadius
                rectProgressPass = RectF(left, top, right, bottom)
                canvas.drawRect(rectProgressPass, progressPaint)

                rectProgressPass = RectF(right - cirlceRadius, top, right + cirlceRadius, bottom)
                canvas.drawArc(rectProgressPass, -90f, 180f, true, progressPaint)
            }
        }
    }

3.5.3说明文字以及进度数值
    private fun drawDesTxt(canvas: Canvas) {
        val des = "已完成${mProgress}%"
        txtPaint.getTextBounds(des, 0, des.length, bounds)
        //获取整个说明文字宽高
        val desW = bounds.width()
        val desH = bounds.height()
        //获取“已完成”三个字宽度
        val lw = txtPaint.measureText(des, 0, 3)

        txtPaint.color = txtDesColor
        canvas.drawText(des, 0, 3, mWidth / 2 - desW / 2f, mHeight / 2 + 
               defTxt2ProgressMargin / 2 + desH / 2, txtPaint)

        txtPaint.color = txtNumColor
        canvas.drawText(des, 3, des.length, mWidth / 2 - desW / 2f + lw, 
               mHeight / 2 + defTxt2ProgressMargin / 2 + desH / 2, txtPaint)
    }

3.6处理View添加Touch事件

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_MOVE -> dealMotionEvent(event.x, event.y, true)//默认滑动事件始终伴随动画
            MotionEvent.ACTION_UP -> dealMotionEvent(event.x, event.y, withAnim)
        }
        return true
    }

    private fun dealMotionEvent(x: Float, y: Float, withAnim: Boolean) {
        val rectLeftHalf = RectF(hMargin, top, hMargin + cirlceRadius, bottom)
        val rectRightHalf = RectF(mWidth - hMargin - cirlceRadius, top, mWidth - hMargin, bottom)
        if (rectLeftHalf.contains(x, y)) setProgress(0f)
        if (rectRightHalf.contains(x, y)) setProgress(100f)
        if (rectProgressArea.contains(x, y)) {
            val progress = (x - hMargin - cirlceRadius) / (mWidth - 2 * hMargin - progressVWidth) * 100f
            setProgress(String.format("%.1f", progress).toFloat(), withAnim)
        }
    }

3.7设置对外调用接口

    fun setWithAnim(withAnim: Boolean) {//设置是否支持动画
        this.withAnim = withAnim
    }

    fun setProgress(progress: Float) = setProgress(progress, withAnim)//方法重载

    fun setProgress(progress: Float, withAnim: Boolean) {//方法重载
        if (progress < 0) mProgress = 0f
        if (progress > 100) mProgress = 100f
        if (withAnim) {
            val animator = ValueAnimator.ofFloat(mProgress, progress)
            animator.addUpdateListener { anim ->
                mProgress = String.format("%.1f", anim.animatedValue).toFloat()
                invalidate()
            }
            animator.interpolator = LinearInterpolator()
            animator.duration = (Math.abs(mProgress - progress) * 50).toLong()
            animator.start()
        } else {
            mProgress = progress
            invalidate()
        }
    }

4.调用

4.1xml调用

    <com.ddlc.hprogress.HProgressView
        android:id="@+id/hpv_progress"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        tangbuzhi:h_margin="20dp"
        tangbuzhi:normal_color="#66000000"
        tangbuzhi:progress="34.5"
        tangbuzhi:progress_color="#ff00ff"
        tangbuzhi:progress_v_width="15dp"
        tangbuzhi:text_des_color="#000fff"
        tangbuzhi:text_num_color="#ff00ff"
        tangbuzhi:text_size="16dp"
        tangbuzhi:text_to_progress_margin="30dp"
        tangbuzhi:with_anim="false"
        tangbuzhi:with_click="false" />

4.2java调用

hpv_progress.setWithAnim(true)
hpv_progress.setWithClick(true)
hpv_progress.setProgress(progress, true)

5.代码托管地址

水平方向带进度条(支持动画)的HProgressView

相关文章

网友评论

      本文标题:自定义View之HProgressView(水平进度条view)

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