1、前言

书接上一回 5分钟带你了解Android Progress Bar 2

上一回说到,要自定义ProgressBar,自定义一个ProgressBar,但功用不如原生的完好版!!首要还是用来了解怎么写一个进展条,然后引出后面的,功用肯定没有原生的那么齐全啦。

假如您有任何疑问、对文章写的不满意、发现错误或许有更好的办法,欢迎在谈论、私信或邮件中提出,十分感谢您的支撑。

看看作用先

5分钟带你了解Android Progress Bar 3

5分钟带你了解Android Progress Bar 3

2、条形ProgressBar简单仿写

(1)预备好Attribute

attr.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <declare-styleable name="ZProgressBar2">
    <attr name="zTextColor" format="color" />
    <attr name="zBarHeight" format="dimension"/>
    <attr name="zMax" format="float" />
    <attr name="zProgress" format="float" />
    <attr name="zProgressColor" format="string" />
    <attr name="zRemainColor" format="string" />
    <attr name="zTextVisibility" format="boolean"/>
  </declare-styleable>
</resources>

随意想点特点啦

(2)新建一个ZProgressBar2.kt

class ZProgressBar2 @JvmOverloads constructor(
  context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {}

姓名随意,新建文件 -> 继承自 View -> 由编译器补全代码。

5分钟带你了解Android Progress Bar 3

选@JvmOverloads的这条!这样Java也能用了

(3)预备好一些变量声明

声明啥,你自己看着办就行。我这儿根本便是预备了,Attribute中特点

/**
 * 默许进展条色彩
 */
private val DEFAULT_PROGRESS_COLOR: Int = Color.parseColor("#FFA5E05B")
private val DEFAULT_REMAIN_COLOR: Int = Color.parseColor("#FFFFFFFF")
private val DEFAULT_TEXT_COLOR: Int = Color.parseColor("#FFFFFFFF")
​
/**
 * 默许进展
 */
private val DEFAULT_MAX: Float = 100f
private val DEFAULT_PROGRESS: Float = 0f
​
/**
 * 进展条最大值
 */
var mMax: Float = DEFAULT_MAX
var mMin: Float = DEFAULT_PROGRESS
/**
 * 进展条当时进展值
 */
var mProgress: Float = DEFAULT_PROGRESS
/**
 * 进展条色彩
 * 完结
 */
var mProgressColor = DEFAULT_PROGRESS_COLOR
/**
 * 进展条色彩
 * 剩下
 */
var mRemainColor = DEFAULT_REMAIN_COLOR
/**
 * 进展条高度
 */
var mBarHeight = 30f.dp
/**
 * 剩下进展区域
 */
private val mRemainRectF = RectF(0f, 0f, 0f, 0f)
​
/**
 * 已完结进展区域
 */
private val mProgressRectF = RectF(0f, 0f, 0f, 0f)
​
/**
 * ANTI_ALIAS_FLAG 抗锯齿
 * isAntiAlias 防抖动
 * Paint.Cap.ROUND 笔画凸出成半圆形
 */
private val mPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
  strokeCap = Paint.Cap.ROUND
  isAntiAlias = true
}
​
/**
 * 进展文字色彩
 */
private var mTextColor = Color.WHITE
private var mTextVisibility = false
private var progressFormat = DecimalFormat("#")
​

(4)预备好读取Attribute的办法

记得在init中调用哈

/**
 * 读取自定义的布局特点
 */
private fun initArr(attrs: AttributeSet?) {
  val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ZProgressBar2)
  typedArray.run {
    mMax = getFloat(R.styleable.ZProgressBar2_zMax, mMax)
    mProgress = getFloat(R.styleable.ZProgressBar2_zProgress, mProgress)
    mProgressColor =
      getColor(R.styleable.ZProgressBar2_zProgressColor, DEFAULT_PROGRESS_COLOR)
    mRemainColor =
      getColor(R.styleable.ZProgressBar2_zRemainColor, DEFAULT_REMAIN_COLOR)
    mBarHeight = getDimension(R.styleable.ZProgressBar2_zBarHeight, 30f.dp)
    mTextColor = getColor(
      R.styleable.ZProgressBar2_zTextColor,
      DEFAULT_REMAIN_COLOR
     )
    mTextVisibility =
      getBoolean(R.styleable.ZProgressBar2_zTextVisibility, true)
   }
  typedArray.recycle()
}
init{
    initArr(attrs)
}

(5)在onDraw中制作

首要代码就在这了,没多少,看注释!

override fun onDraw(canvas: Canvas?) {
  super.onDraw(canvas)
  calculateProgressRect()
  canvas?.run {
      //先画剩下的,要让进展压在上面
    mPaint.color = mRemainColor
    //进展和剩下进展,便是两个圆角方形叠在一同
    drawRoundRect(mRemainRectF, mBarHeight / 2, mBarHeight / 2, mPaint)
    //再画进展
    mPaint.color = mProgressColor
    drawRoundRect(mProgressRectF, mBarHeight / 2, mBarHeight / 2, mPaint)
    //然后文字在最上层
    mPaint.textSize = mBarHeight * 0.5f
    //格式化Progress
    var mCurrentDrawText: String = progressFormat.format(mProgress * 100 / mMax)
    //画文字的根本操作,先测一下宽度
    val mDrawTextWidth = mPaint.measureText(mCurrentDrawText)
    //要判别下进展的宽度,够不够画文字出来
    if (mTextVisibility && mProgress > 0 && mProgressRectF.right > mDrawTextWidth) {
        mPaint.color = mTextColor
      drawText(
        mCurrentDrawText,
        mProgressRectF.right - mDrawTextWidth - mBarHeight * 0.4f,
          //descent/ascent 关于文字的高度能够去看看相关文章
         (height / 2.0f - (mPaint.descent() + mPaint.ascent()) / 2.0f).toInt().toFloat(),
        mPaint
       )
     }
   }
}
/**
 * 很惯例的核算一个View的四个坐标的办法
 */
private fun calculateProgressRect() {
  val ttop = (height - mBarHeight) / 2.0f
  val bbottom = (height + mBarHeight) / 2.0f
  mProgressRectF.run {
    left = paddingLeft.toFloat()
    top = ttop
    /**
     * 核算已完结进展的长度
     */
    right = (width - paddingLeft - paddingRight) / (mMax * 1.0f) * mProgress + paddingLeft
    bottom = bbottom
   }
  mRemainRectF.run {
    left = paddingLeft.toFloat()
    top = ttop
    right = (width - paddingRight).toFloat()
    bottom = bbottom
   }
}

(6)露出更新办法

重写下set办法就好了,都去调用invalidate()办法。

/**
 * 进展条最大值
 */
var mMax: Float = DEFAULT_MAX
  set(value) {
    field = value
    invalidate()
   }
​
var mMin: Float = DEFAULT_PROGRESS
  set(value) {
    field = value
    invalidate()
   }
​
/**
 * 进展条当时进展值
 */
@set:Synchronized
var mProgress: Float = DEFAULT_PROGRESS
  set(value) {
    if (value == field) {
      // No change from current.
      return
     }
          //不能超过最大值,最小值啊
    field = value.coerceIn(mMin, mMax)
    invalidate()
   }
​
/**
 * 进展条色彩
 * 完结
 */
var mProgressColor = DEFAULT_PROGRESS_COLOR
  set(value) {
    field = value
    invalidate()
   }
​
/**
 * 进展条色彩
 * 剩下
 */
var mRemainColor = DEFAULT_REMAIN_COLOR
  set(value) {
    field = value
    invalidate()
   }
​
/**
 * 进展条高度
 */
var mBarHeight = 30f.dp
  set(value) {
    field = value
    invalidate()
   }

没啦,就这么多,一个看起来差不多的就行了,简单版~

(7)完好代码

下面是全部代码,以及调用代码

import android.animation.ObjectAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.util.AttributeSet
import android.view.View
import com.nf.framework.utillib.ext.dp
import com.nf.module_test.R
import java.text.DecimalFormat
/**
 * 仿制ProgressBar
 */
class ZProgressBar2 @JvmOverloads constructor(
  context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
  /**
   * 默许进展条色彩
   */
  private val DEFAULT_PROGRESS_COLOR: Int = Color.parseColor("#FFA5E05B")
  private val DEFAULT_REMAIN_COLOR: Int = Color.parseColor("#FFFFFFFF")
  private val DEFAULT_TEXT_COLOR: Int = Color.parseColor("#FFFFFFFF")
​
  /**
   * 默许进展
   */
  private val DEFAULT_MAX: Float = 100f
  private val DEFAULT_PROGRESS: Float = 0f
​
  /**
   * 进展条最大值
   */
  var mMax: Float = DEFAULT_MAX
    set(value) {
      field = value
      invalidate()
     }
​
  var mMin: Float = DEFAULT_PROGRESS
    set(value) {
      field = value
      invalidate()
     }
​
  /**
   * 进展条当时进展值
   */
  @set:Synchronized
  var mProgress: Float = DEFAULT_PROGRESS
    set(value) {
      if (value == field) {
        // No change from current.
        return
       }
      field = value.coerceIn(mMin, mMax)
      invalidate()
     }
​
  /**
   * 进展条色彩
   * 完结
   */
  var mProgressColor = DEFAULT_PROGRESS_COLOR
    set(value) {
      field = value
      invalidate()
     }
​
  /**
   * 进展条色彩
   * 剩下
   */
  var mRemainColor = DEFAULT_REMAIN_COLOR
    set(value) {
      field = value
      invalidate()
     }
​
  /**
   * 进展条高度
   */
  var mBarHeight = 30f.dp
    set(value) {
      field = value
      invalidate()
     }
​
  /**
   * 剩下进展区域
   */
  private val mRemainRectF = RectF(0f, 0f, 0f, 0f)
​
  /**
   * 已完结进展区域
   */
  private val mProgressRectF = RectF(0f, 0f, 0f, 0f)
​
  /**
   * ANTI_ALIAS_FLAG 抗锯齿
   */
  private val mPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
    /**
     * 笔画凸出成半圆形
     */
    strokeCap = Paint.Cap.ROUND
    /**
     * 防抖动
     */
    isAntiAlias = true
   }
​
  /**
   * 进展文字色彩
   */
  private var mTextColor = Color.WHITE
  private var mTextVisibility = false
  private var progressFormat = DecimalFormat("#")
​
  init {
    initArr(attrs)
   }
  /**
   * 读取自定义的布局特点
   */
  private fun initArr(attrs: AttributeSet?) {
    val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ZProgressBar2)
    typedArray.run {
      mMax = getFloat(R.styleable.ZProgressBar2_zMax, mMax)
      mProgress = getFloat(R.styleable.ZProgressBar2_zProgress, mProgress)
      mProgressColor =
        getColor(R.styleable.ZProgressBar2_zProgressColor, DEFAULT_PROGRESS_COLOR)
      mRemainColor =
        getColor(R.styleable.ZProgressBar2_zRemainColor, DEFAULT_REMAIN_COLOR)
      mBarHeight = getDimension(R.styleable.ZProgressBar2_zBarHeight, 30f.dp)
      mTextColor = getColor(
        R.styleable.ZProgressBar2_zTextColor,
        DEFAULT_REMAIN_COLOR
       )
      mTextVisibility =
        getBoolean(R.styleable.ZProgressBar2_zTextVisibility, true)
     }
    typedArray.recycle()
   }
​
  override fun onDraw(canvas: Canvas?) {
    super.onDraw(canvas)
    calculateProgressRect()
    canvas?.run {
      mPaint.color = mRemainColor
      drawRoundRect(mRemainRectF, mBarHeight / 2, mBarHeight / 2, mPaint)
      mPaint.color = mProgressColor
      drawRoundRect(mProgressRectF, mBarHeight / 2, mBarHeight / 2, mPaint)
      mPaint.textSize = mBarHeight * 0.5f
      var mCurrentDrawText: String = progressFormat.format(mProgress * 100 / mMax)
      mPaint.color = mTextColor
      val mDrawTextWidth = mPaint.measureText(mCurrentDrawText)
      if (mTextVisibility && mProgress > 0 && mProgressRectF.right > mDrawTextWidth) {
        drawText(
          mCurrentDrawText,
          mProgressRectF.right - mDrawTextWidth - mBarHeight * 0.4f,
           (height / 2.0f - (mPaint.descent() + mPaint.ascent()) / 2.0f).toInt().toFloat(),
          mPaint
         )
       }
     }
​
   }
​
  private fun calculateProgressRect() {
    val ttop = (height - mBarHeight) / 2.0f
    val bbottom = (height + mBarHeight) / 2.0f
    mProgressRectF.run {
      left = paddingLeft.toFloat()
      top = ttop
      /**
       * 核算已完结进展的长度
       */
      right = (width - paddingLeft - paddingRight) / (mMax * 1.0f) * mProgress + paddingLeft
      bottom = bbottom
     }
    mRemainRectF.run {
      left = paddingLeft.toFloat()
      top = ttop
      right = (width - paddingRight).toFloat()
      bottom = bbottom
     }
   }
}
<com.nf.module_test.seekbar.custom.ZProgressBar2
  android:id="@+id/zpb_01"
  android:layout_width="match_parent"
  android:layout_height="40dp"
  app:zBarHeight="40dp"
  app:zTextColor="#ffffff"
  app:zMax="100"
  app:zProgress="50" />

作用如下

5分钟带你了解Android Progress Bar 3

3、圆形ProgressBar

嘻嘻嘻,直的写出来了,圆的也的有一个吧,交互也得有吧?就在这个基础上,咱们再扩展下

(1)依然咱们需求预备好Attribute

这儿咱们直接在本来的基础上加一点吧

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <declare-styleable name="ZProgressBar2">
    <attr name="zTextColor" format="color" />
    <attr name="zBarHeight" format="dimension" />
    <attr name="zMax" format="float" />
    <attr name="zProgress" format="float" />
    <attr name="zProgressColor" format="string" />
    <attr name="zRemainColor" format="string" />
    <attr name="zTextVisibility" format="boolean" />
    <attr name="zShape" format="integer">
      <enum name="line" value="0"/>
      <enum name="oval" value="1"/>
    </attr>
    <attr name="zFullDegree" format="integer" />
    <attr name="zAnimationDuration" format="integer" />
    <attr name="zDragEnabled" format="boolean" />
  </declare-styleable>
</resources>

(2)持续在ZProgressBar2中添加一些变量声明

还有一些变量在后面看吧

/**
 * 样式
 */
private var mShape = DEFAULT_SHAPE
/**
 *  进展条的视点
 */
private var mMaxDegree: Int = DEFAULT_FULL_DEGREE
  set(value) {
    field = value.coerceIn(0,360)
   }
/**
 * Thumb对象
 */
private var mThumbDraw: Drawable? = null
/**
 * Thumb对象约束宽高
 */
private var mThumbRadius = DEFAULT_BAR_WIDTH * 1.2f
/**
 * TODO
 */
private var mAnimationDuration: Int = 0
/**
 * 是否能够拖动
 */
private var mDragEnabled: Boolean = true
/**
 * 圆弧的半径
 */
private var mCircleRadius: Float = 0f
/**
 * 圆弧圆心方位
 */
private var centerX: Int = 0
/**
 * 圆弧圆心方位
 */
private var centerY: Int = 0
private var mWidth: Int = 0
private var mHeight: Int = 0

(3)预备好读取Attribute的办法

这儿咱们接着之前的写好就行了

private fun initArr(attrs: AttributeSet?) {
  val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ZProgressBar2)
  typedArray.run {
    mMax = getFloat(R.styleable.ZProgressBar2_zMax, mMax)
    mProgress = getFloat(R.styleable.ZProgressBar2_zProgress, mProgress)
    mProgressColor = getColor(R.styleable.ZProgressBar2_zProgressColor, DEFAULT_PROGRESS_COLOR)
    mRemainColor = getColor(R.styleable.ZProgressBar2_zRemainColor, DEFAULT_REMAIN_COLOR)
    mBarHeight = getDimension(R.styleable.ZProgressBar2_zBarHeight, DEFAULT_BAR_WIDTH)
    mTextColor = getColor(R.styleable.ZProgressBar2_zTextColor,DEFAULT_REMAIN_COLOR)
    mTextVisibility = getBoolean(R.styleable.ZProgressBar2_zTextVisibility, tr
    mMaxDegree = getInteger(R.styleable.ZProgressBar2_zFullDegree, DEFAULT_FULL_DEGREE)
    mAnimationDuration = getInteger(R.styleable.ZProgressBar2_zAnimationDuration, 500)
    mDragEnabled = getBoolean(R.styleable.ZProgressBar2_zDragEnabled, DEFAULT_ENABLE_TOUCH)
    mShape = getInteger(R.styleable.ZProgressBar2_zShape, DEFAULT_SHAPE)
    mThumbDraw = getDrawable(R.styleable.ZProgressBar2_zThumbDrawable)
    mThumbRadius = getDimension(R.styleable.ZProgressBar2_zThumbRadius, DEFAULT_BAR_WIDTH * 1.2f)
   }
  typedArray.recycle()
}

(4)处理onMeasure

/**
 * left:矩形左面线条离 y 轴的间隔
 * top:矩形上面线条离 x 轴的间隔
 * right:矩形右边线条离 y 轴的间隔
 * bottom:矩形底部线条离 x 轴的间隔
 * 这儿我没有专门去处理layout_height、width,只处理固定宽高,交给你自己做啦
 */
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    if (mWidth == 0 || mHeight == 0) {
        mWidth = measuredWidth
        mHeight = measuredHeight
        //两者取最大
        mCircleRadius = mWidth.coerceAtMost(mHeight) / 2f
        //给BarHeight一个默许值,假如没有
        if (mBarHeight <= 0) mBarHeight = mCircleRadius / 12f
        //实践的制作需求考虑到Thumb可能会超出中心圆一点点,所以减掉,假如没有就去减掉进展条的
        mCircleRadius -= if (mThumbDraw == null) {
            mBarHeight * 0.6f
        } else {
            mThumbRadius * 0.6f
        }
        centerX = mWidth / 2
        centerY = mHeight / 2
       	//进展的弧形便是靠这个
        mProgressRectF.run {
            left = centerX - mCircleRadius
            top = centerY - mCircleRadius
            right = centerX + mCircleRadius
            bottom = centerY + mCircleRadius
        }
    }
}

(5)处理下直线和圆的onDraw

这儿经过读取的mShape,来判别是显示哪个。onDrawDir便是前面写的直线bar啦

override fun onDraw(canvas: Canvas?) {
    super.onDraw(canvas)
    if (mShape == 0) {
        onDrawDir(canvas)
    } else {
        onDrawArc(canvas)
    }
}

思考下,由于咱们是支撑指定最大弧度的,所以进展条其实便是一个扇形,然后只画边就行了(不然你也能够用两个圆堆叠达到相同的作用)

一个扇形画当时进展,一个画剩下进展,完事。再到中间画一个文字,进展条前端画一个thumb~~~

private fun onDrawArc(canvas: Canvas?) {
    canvas?.run {
        /**
         * 进展条起始点
         * shr 1 便是/2的意思
         */
        val startAngle: Float = (90 + ((360 - mMaxDegree) shr 1)).toFloat()
        //当时进展的百分比
        val sweep1: Float = mMaxDegree * (mProgress / mMax)
        //剩下进展的百分比
        val sweep2: Float = mMaxDegree - sweep1
        //由于咱们是画进展条的,只需求画周边就行了,所用空心 描边
        mBarPaint.style = Paint.Style.STROKE
        //画笔的strokeWidth就相当于进展条宽度了
        mBarPaint.strokeWidth = mBarHeight
        mBarPaint.color = mRemainColor
        //很简单,制作区域咱们现已预备好了mProgressRectF
        //剩下起点用’当时进展起点+初始起点‘便是剩下进展的起点,结束明显便是sweep2
        drawArc(mProgressRectF, startAngle + sweep1, sweep2, false, mBarPaint)
        mBarPaint.color = mProgressColor
        //当时进展起点当然便是初始起点,结束当然是sweep1
        drawArc(mProgressRectF, startAngle, sweep1, false, mBarPaint)
        /**
         * 依据三角函数来核算出thumb的XY值
         * PS:你可能需求必定的数学知识和Android的坐标系知识
         * 假如你不会,记住就行,由于都相同~或许留言
         */
        val progressRadians =
            (((360.0f - mMaxDegree) /2 + sweep1) / 180 * Math.PI).toFloat()
        val thumbX: Float =
            centerX - mCircleRadius * sin(progressRadians.toDouble()).toFloat()
        val thumbY: Float =
            centerY + mCircleRadius * cos(progressRadians.toDouble()).toFloat()
        /**
         * 依据thumb的半径画出drawable对象
         */
        mThumbDraw?.let {
            it.setBounds(
                (thumbX - mThumbRadius / 2).toInt(), (thumbY - mThumbRadius / 2).toInt(),
                (thumbX + mThumbRadius / 2).toInt(), (thumbY + mThumbRadius / 2).toInt())
            it.draw(canvas)
        }
        /**
         * 文字制作
         */
        if (mTextVisibility) {
            mTextPaint.textSize = (mCircleRadius.toInt() shr 1).toFloat()
            mTextPaint.color = Color.parseColor("#FFCB47")
            val textProgress: String = progressFormat.format(100f * mProgress / mMax)
            val textLen: Float = mTextPaint.measureText(textProgress)
            mTextPaint.getTextBounds("8", 0, 1, mTextBounds)
            val textProgressHeight: Int = mTextBounds.height()
            val extra: Float =
                if (textProgress.startsWith("1")) -mTextPaint.measureText("1") / 2 else 0F
            /**
             * 首要是核算文字的左上角坐标
             * 让它画在中间
             */
            drawText(
                textProgress,
                centerX - textLen / 2 + extra,
                centerY + textProgressHeight / 2f,
                mTextPaint
            )
            /**
             * shr 2 便是/4啦
             */
            mTextPaint.textSize = (mCircleRadius.toInt() shr 2).toFloat()
            drawText(
                "%",
                centerX + textLen / 2 + extra + 5,
                centerY + textProgressHeight / 2f,
                mTextPaint
            )
        }
    }
}

到这儿,制作就根本完毕了。接下啦咱们要考虑点击事情的问题了

(6)点击事情的处理

咱们直接分为两种,一种点击,一种按住滑动。话不多说,全在注释里

private var isDragging = true
private var lastProgress = -1F
/**
 * mDragEnabled变量用于控制是否启用拖拽功用
 * checkOnArc办法判别点击是否在圆弧上;
 * thumbProgress办法依据点击的方位核算进展并将进展更新到控件上;
 * isDragging变量用于标识当时是否在拖拽。
 * 当用户点击时,假如检测到点击方位在圆弧上,则执行进展核算并设置isDragging变量为true。
 * 当用户拖动时,假如当时正在拖拽,则执行进展核算;
 * 当用户抬起手指时,将isDragging设置为false。
 * 在办法结束,返回true,表明该办法现已处理了该事情,不需求再向上传递。
 */
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
  if (!mDragEnabled) {
    return super.onTouchEvent(event)
   }
  val currentX = event.x
  val currentY = event.y
  when (event.action) {
    MotionEvent.ACTION_DOWN ->
      if (checkOnArc(currentX, currentY)) {
        thumbProgress(currentX, currentY)
        isDragging = true
       }
    MotionEvent.ACTION_MOVE -> if (isDragging) {
      thumbProgress(currentX, currentY)
     }
    MotionEvent.ACTION_UP -> isDragging = false
   }
  return true
}
​
/**
 * 保证滑块的拖动不会在进展条的相反方向进行。
 * 首要,经过核算当时点相对于中心点的极角
 * 并将其转换为当时进展。
 * 接着,将该进展与当时最终一次进展(lastProgress)进行比较
 * 假如两者差值小于最大值的一半,则更新当时进展(mProgress)为该进展;
 * 不然,当时进展不变。
 */
private fun thumbProgress(currentX: Float, currentY: Float) {
  var nextProgress = calculateDegree(currentX, currentY) / mMaxDegree * mMax
  nextProgress = nextProgress.coerceAtMost(mMax).coerceAtLeast(mMin)
  val delta = abs(nextProgress - lastProgress)
  if (delta < mMax / 2 || lastProgress == -1F) {
    lastProgress = nextProgress
    mProgress = nextProgress
   }
}
​
/**
 * 检查间隔是否在圆弧的内圈和外圈之间,并检查视点是否在圆弧的开端和完毕方位的必定范围内
 * 首要调用calculateDistance函数核算给定点(currentX,currentY)与(centerX,centerY)之间的间隔。
 * 然后调用calculateDegree函数核算给定点与圆心之间的视点。
 * 最终,该函数经过比较这个间隔和视点与圆弧上的特定范围,来判别给定的点是否在圆弧范围内。
 * return 给定的点(currentX,currentY)是否在一个弧形范围内
 */
private fun checkOnArc(currentX: Float, currentY: Float): Boolean {
  val distance = calculateDistance(currentX, currentY, centerX.toFloat(), centerY.toFloat())
  val degree = calculateDegree(currentX, currentY)
  return distance > mCircleRadius - mBarHeight * 3 //点击区域弧内
      && distance < mCircleRadius + mBarHeight * 3 //点击区域弧外
      && degree >= -8 && //圆弧开端方位
      degree <= mMaxDegree + 8 //圆弧完毕方位
}
​
/**
 * 核算两点(x1,y1)和(x2,y2)之间的间隔
 * 经过公式√((x1-x2)+(y1-y2))核算两点间的欧几里得间隔
 */
private fun calculateDistance(x1: Float, y1: Float, x2: Float, y2: Float): Float {
  return sqrt(((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)).toDouble()).toFloat()
}
​
/**
 *  核算当时点(currentX,currentY)与圆心(centerX,centerY)之间的视点
 *  首要,经过反正切函数atan核算出视点a1
 *  接着,依据当时点的方位与圆心的联系,分三种状况谈论
 *  假如当时点在圆心下方,则在a1上加180
 *  假如当时点在圆心上方且在圆心右方,则在a1上加360
 *  最终,减去360与mMaxDegree的差的一半,并返回结果
 *  这样就得到了当时点相对于圆心的视点值,并约束在了mMaxDegree范围内。
 */
private fun calculateDegree(currentX: Float, currentY: Float): Float {
  var a1 =
     (atan((1.0f * (centerX - currentX) / (currentY - centerY)).toDouble()) / Math.PI * 180).toFloat()
  if (currentY < centerY) {
    a1 += 180f
   } else if (currentY > centerY && currentX > centerX) {
    a1 += 360f
   }
  return a1 - (360 - mMaxDegree) / 2
}

(7)完结!

调用下~

    <LinearLayout
      android:orientation="horizontal"
      android:layout_width="match_parent"
      android:layout_gravity="center"
      android:gravity="center"
      android:layout_height="wrap_content">
      <com.nf.module_test.seekbar.custom.ZProgressBar2
        android:id="@+id/zpb_01"
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:zBarHeight="5dp"
        app:zTextColor="#ffffff"
        app:zShape="oval"
        app:zFullDegree="90"
        app:zThumbDrawable="@drawable/icon_seekbar_thum"
        app:zMax="100"
        app:zProgress="50" />
​
      <com.nf.module_test.seekbar.custom.ZProgressBar2
        android:id="@+id/zpb_02"
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:zBarHeight="5dp"
        app:zFullDegree="180"
        app:zMax="100"
        app:zProgress="50"
        app:zShape="oval"
        app:zTextColor="#ffffff"
        app:zThumbDrawable="@drawable/icon_seekbar_thum" />
​
      <com.nf.module_test.seekbar.custom.ZProgressBar2
        android:id="@+id/zpb_03"
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:zBarHeight="5dp"
        app:zTextColor="#ffffff"
        app:zShape="oval"
        app:zFullDegree="360"
        app:zThumbDrawable="@drawable/icon_seekbar_thum"
        app:zMax="100"
        app:zProgress="50" />
    </LinearLayout>

看看作用

5分钟带你了解Android Progress Bar 3

完好代码ZProgressBar2.kt 和 完好代码attr.xml

还有许多功用,都没有做完,不过这些看完,相信你现已能够自己接着来了,个人认为,自定义View的中心都在怎么准确的核算View的坐标,那么这个圆形的相关,根本都在三角函数上啦。

结束

这个系列现已3篇了,还有许多奇怪的进展条我都没有写,由于写了3篇了我感觉我的脑子里都是进展条,所以先暂停一会啦,当然假如谁真的很需求下一篇,谈论、私信、邮箱我,我会速更第四篇,嘻嘻x.x

那么下篇见

“开启成长之旅!这是我参与「日新方案 2 月更文挑战」的第 3 天,点击查看活动概况”