溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Android如何自定義球型水波紋帶圓弧進度效果

發(fā)布時間:2021-09-27 13:56:22 來源:億速云 閱讀:145 作者:小新 欄目:編程語言

這篇文章主要為大家展示了“Android如何自定義球型水波紋帶圓弧進度效果”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Android如何自定義球型水波紋帶圓弧進度效果”這篇文章吧。

需求

如下,實現(xiàn)一個圓形水波紋,帶進度,兩層水波紋需要漸變顯示,且外圍有一個圓弧進度。

思路

外圍圓弧進度:可以通過canvas.drawArc()實現(xiàn)。由于圓弧需要實現(xiàn)漸變,可以通過給畫筆設(shè)置shader(SweepGradient)渲染,為了保證圓弧起始的顏色值始終一致,需要動態(tài)調(diào)整shader的參數(shù)。具體參見

SweepGradient(centerX.toFloat(), centerY.toFloat(), circleColors[0], floatArrayOf(0f, value / 100f))

第四個參數(shù)需要根據(jù)當(dāng)前進度填寫對應(yīng)數(shù)據(jù)比例。不懂的同學(xué)可以自行百度查閱。

水波紋的實現(xiàn):直接使用貝塞爾曲線Path.quadTo()實現(xiàn),通過拉伸水平直線繪制波浪效果??梢酝ㄟ^控制拉伸點(waveAmplitude)距離水平線的高度,達到波浪高度的控制。至于波浪的移動,可以通過移動平移水平線的起始位置來實現(xiàn),在使用動畫循環(huán)即可,為了能夠穩(wěn)定的顯示,繪制波浪時需要嚴格繪制整數(shù)倍周期的波浪。

園形的實現(xiàn):繪制一個完整的圓形,然后通過Path.op()合并裁剪水波紋path。注意點就是Android6有個坑,使用該方法會有明顯的抖動,為了解決該問題,我的做法是多畫一層圓弧以掩蓋此抖動。

生命周期的控制:為了減少某些時刻CPU的損耗,通過控制變量自定義lifeDelegate(基于kotlin的代理模式實現(xiàn))來控制動畫的開始暫停。由于筆者使用的框架基于MVVM,所以代碼就沒有使用attrs控制屬性,這里就不做過多的修改了。

整體實現(xiàn)

class WaveView(context: Context, attributeSet: AttributeSet? = null) : View(context, attributeSet) { companion object {  const val RESUME = 0x1  const val STOP = 0x2  const val DESTROY = 0x3 } private var mWidth = 0 //控件整體寬度 private var mHeight = 0 //控件整體高度 //控件中心位置,x,y坐標(biāo) private var centerX = 0 private var centerY = 0 private var outerRadius = 0//外圈圓環(huán)的半徑 private var innerRadius = 250f//內(nèi)部圓圈的半徑 private var radiusDist = 50f//內(nèi)外圓圈的半徑差距 private var fWaveShader: LinearGradient? = null private var sWaveShader: LinearGradient? = null private var wavePath = Path() private var waveCirclePath = Path() private val waveNum = 2 //波浪的漸變顏色數(shù)組 private val waveColors by lazy {  arrayListOf(    //深紅色    intArrayOf(Color.parseColor("#E8E6421A"), Color.parseColor("#E2E96827")),    intArrayOf(Color.parseColor("#E8E6421A"), Color.parseColor("#E2F19A7F")),    //橙色    intArrayOf(Color.parseColor("#E8FDA085"), Color.parseColor("#E2F6D365")),    intArrayOf(Color.parseColor("#E8FDA085"), Color.parseColor("#E2F5E198")),    //綠色    intArrayOf(Color.parseColor("#E8009EFD"), Color.parseColor("#E22AF598")),    intArrayOf(Color.parseColor("#E8009EFD"), Color.parseColor("#E28EF0C6"))  ) } //外圍圓環(huán)的漸變色 private val circleColors by lazy {  arrayListOf(    //深紅色    intArrayOf(Color.parseColor("#FFF83600"), Color.parseColor("#FFF9D423")),    //橙色    intArrayOf(Color.parseColor("#FFFDA085"), Color.parseColor("#FFF6D365")),    //綠色    intArrayOf(Color.parseColor("#FF2AF598"), Color.parseColor("#FF009EFD"))  ) } private val wavePaint by lazy {  val paint = Paint()  paint.isAntiAlias = true  paint.strokeWidth = 1f  paint } //波浪高度比例 private var waveWaterLevelRatio = 0f //波浪的振幅 private var waveAmplitude = 0f //波浪最大振幅高度 private var maxWaveAmplitude = 0f //外圍圓圈的畫筆 private val outerCirclePaint by lazy {  val paint = Paint()  paint.strokeWidth = 20f  paint.strokeCap = Paint.Cap.ROUND  paint.style = Paint.Style.STROKE  paint.isAntiAlias = true  paint } private val outerNormalCirclePaint by lazy {  val paint = Paint()  paint.strokeWidth = 20f  paint.color = Color.parseColor("#FFF2F3F3")  paint.style = Paint.Style.STROKE  paint.isAntiAlias = true  paint } private val bgCirclePaint by lazy {  val paint = Paint()  paint.color = Color.parseColor("#FFF6FAFF")  paint.style = Paint.Style.FILL  paint.isAntiAlias = true  paint } private val textPaint by lazy {  val paint = Paint()  paint.style = Paint.Style.FILL  paint.textAlign = Paint.Align.CENTER  paint.isFakeBoldText = true  paint.isAntiAlias = true  paint } private val ringPaint by lazy {  val paint = Paint()  paint.style = Paint.Style.STROKE  paint.color = Color.WHITE  paint.isAntiAlias = true  paint } //外圍圓圈所在的矩形 private val outerCircleRectf by lazy {  val rectF = RectF()  rectF.set(    centerX - outerRadius + outerCirclePaint.strokeWidth,    centerY - outerRadius + outerCirclePaint.strokeWidth,    centerX + outerRadius - outerCirclePaint.strokeWidth,    centerY + outerRadius - outerCirclePaint.strokeWidth  )  rectF } //外圍圓圈的顏色漸變器矩陣,用于從90度開啟漸變,由于線條頭部有個小圓圈會導(dǎo)致顯示差異,因此從88度開始繪制 private val sweepMatrix by lazy {  val matrix = Matrix()  matrix.setRotate(88f, centerX.toFloat(), centerY.toFloat())  matrix } //進度 0-100 var percent = 0  set(value) {   field = value   waveWaterLevelRatio = value / 100f   //y = -4 * x2 + 4x拋物線計算振幅,水波紋振幅規(guī)律更加真實   waveAmplitude =     (-4 * (waveWaterLevelRatio * waveWaterLevelRatio) + 4 * waveWaterLevelRatio) * maxWaveAmplitude//   waveAmplitude = if (value < 50) 2f * waveWaterLevelRatio * maxWaveAmplitude else (-2 * waveWaterLevelRatio + 2) * maxWaveAmplitude   val shader = when (value) {    in 0..46 -> {     fWaveShader = LinearGradient(       0f, mHeight.toFloat(), 0f, mHeight * (1 - waveWaterLevelRatio),       waveColors[0],       null, Shader.TileMode.CLAMP     )     sWaveShader = LinearGradient(       0f, mHeight.toFloat(), 0f, mHeight * (1 - waveWaterLevelRatio),       waveColors[1],       null, Shader.TileMode.CLAMP     )     SweepGradient(       centerX.toFloat(),       centerY.toFloat(),       circleColors[0],       floatArrayOf(0f, value / 100f)     )    }    in 47..54 -> {     fWaveShader = LinearGradient(       0f, mHeight.toFloat(), 0f, mHeight * (1 - waveWaterLevelRatio),       waveColors[2],       null, Shader.TileMode.CLAMP     )     sWaveShader = LinearGradient(       0f, mHeight.toFloat(), 0f, mHeight * (1 - waveWaterLevelRatio),       waveColors[3],       null, Shader.TileMode.CLAMP     )     SweepGradient(       centerX.toFloat(),       centerY.toFloat(),       circleColors[1],       floatArrayOf(0f, value / 100f)     )    }    else -> {     fWaveShader = LinearGradient(       0f, mHeight.toFloat(), 0f, mHeight * (1 - waveWaterLevelRatio),       waveColors[4],       null, Shader.TileMode.CLAMP     )     sWaveShader = LinearGradient(       0f, mHeight.toFloat(), 0f, mHeight * (1 - waveWaterLevelRatio),       waveColors[5],       null, Shader.TileMode.CLAMP     )     SweepGradient(       centerX.toFloat(),       centerY.toFloat(),       circleColors[2],       floatArrayOf(0f, value / 100f)     )    }   }   shader.setLocalMatrix(sweepMatrix)   outerCirclePaint.shader = shader   invalidate()  } private val greedTip = "Greed Index" //文本的字體大小 private var percentSize = 80f private var greedSize = 30f private var textColor = Color.BLACK //外圍圓圈的畫筆大小 private var outerStrokeWidth = 10f private var fAnimatedValue = 0f private var sAnimatedValue = 0f //動畫 private val fValueAnimator by lazy {  val valueAnimator = ValueAnimator()  valueAnimator.duration = 1500  valueAnimator.repeatCount = ValueAnimator.INFINITE  valueAnimator.interpolator = LinearInterpolator()  valueAnimator.setFloatValues(0f, waveWidth)  valueAnimator.addUpdateListener { animation ->   fAnimatedValue = animation.animatedValue as Float   invalidate()  }  valueAnimator } private val sValueAnimator by lazy {  val valueAnimator = ValueAnimator()  valueAnimator.duration = 2000  valueAnimator.repeatCount = ValueAnimator.INFINITE  valueAnimator.interpolator = LinearInterpolator()  valueAnimator.setFloatValues(0f, waveWidth)  valueAnimator.addUpdateListener { animation ->   sAnimatedValue = animation.animatedValue as Float   invalidate()  }  valueAnimator } //一小段完整波浪的寬度 private var waveWidth = 0f var lifeDelegate by Delegates.observable(0) { _, old, new ->  when (new) {   RESUME -> onResume()   STOP -> onPause()   DESTROY -> onDestroy()  } } //設(shè)置中間進度文本的字體大小 fun setPercentSize(size: Float) {  percentSize = size  invalidate() } //設(shè)置中間提示文本的字體大小 fun setGreedSize(size: Float) {  greedSize = size  invalidate() } //設(shè)置文本顏色 fun setTextColor(color: Int) {  textColor = color  textPaint.color = textColor  invalidate() } //設(shè)置外圍圓圈的寬度 fun setOuterStrokeWidth(width: Float) {  outerStrokeWidth = width  outerCirclePaint.strokeWidth = outerStrokeWidth  outerNormalCirclePaint.strokeWidth = outerStrokeWidth  invalidate() } //設(shè)置內(nèi)圓半徑 fun setInnerRadius(radius: Float) {  innerRadius = radius  invalidate() } override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {  super.onSizeChanged(w, h, oldw, oldh)  mWidth = width - paddingStart - paddingEnd  mHeight = height - paddingTop - paddingBottom  centerX = mWidth / 2  centerY = mHeight / 2  outerRadius = mWidth.coerceAtMost(mHeight) / 2  radiusDist = outerRadius - innerRadius  waveWidth = mWidth * 1.8f  maxWaveAmplitude = mHeight * 0.15f } private fun onResume() {  if (fValueAnimator.isStarted) {   animatorResume()  } else {   fValueAnimator.start()   sValueAnimator.start()  } } private fun animatorResume() {  if (fValueAnimator.isPaused || !fValueAnimator.isRunning) {   fValueAnimator.resume()  }  if (sValueAnimator.isPaused || !sValueAnimator.isRunning) {   sValueAnimator.resume()  } } private fun onPause() {  if (fValueAnimator.isRunning) {   fValueAnimator.pause()  }  if (sValueAnimator.isRunning) {   sValueAnimator.pause()  } } private fun onDestroy() {  fValueAnimator.cancel()  sValueAnimator.cancel() } //當(dāng)前窗口銷毀時,回收動畫資源 override fun onDetachedFromWindow() {  onDestroy()  super.onDetachedFromWindow() } override fun onDraw(canvas: Canvas) {  drawCircle(canvas)  drawWave(canvas)  drawText(canvas) } private fun drawWave(canvas: Canvas) {  //波浪當(dāng)前高度  val level = (1 - waveWaterLevelRatio) * innerRadius * 2 + radiusDist  //繪制所有波浪  for (num in 0 until waveNum) {   //重置path   wavePath.reset()   waveCirclePath.reset()   var startX = if (num == 0) {//第一條波浪的起始位置    wavePath.moveTo(-waveWidth + fAnimatedValue, level)    -waveWidth + fAnimatedValue   } else {//第二條波浪的起始位置    wavePath.moveTo(-waveWidth + sAnimatedValue, level)    -waveWidth + sAnimatedValue   }   while (startX < mWidth + waveWidth) {    wavePath.quadTo(      startX + waveWidth / 4,      level + waveAmplitude,      startX + waveWidth / 2,      level    )    wavePath.quadTo(      startX + waveWidth / 4 * 3,      level - waveAmplitude,      startX + waveWidth,      level    )    startX += waveWidth   }   wavePath.lineTo(startX, mHeight.toFloat())   wavePath.lineTo(0f, mHeight.toFloat())   wavePath.close()   waveCirclePath.addCircle(     centerX.toFloat(),     centerY.toFloat(),     innerRadius,     Path.Direction.CCW   )   waveCirclePath.op(wavePath, Path.Op.INTERSECT)   //繪制波浪漸變色   wavePaint.shader = if (num == 0) {    sWaveShader   } else {    fWaveShader   }   canvas.drawPath(waveCirclePath, wavePaint)  }  //Fixme android6設(shè)置Path.op存在明顯抖動,因此多畫一圈圓環(huán)  val ringWidth = outerRadius - outerStrokeWidth - innerRadius  ringPaint.strokeWidth = ringWidth / 2  canvas.drawCircle(centerX.toFloat(), centerY.toFloat(), innerRadius + ringWidth / 4, ringPaint) } private fun drawText(canvas: Canvas) {  //繪制進度文字  textPaint.isFakeBoldText = true  textPaint.textSize = percentSize  canvas.drawText(    percent.toString(),    centerX.toFloat(),    centerY.toFloat() + textPaint.textSize / 2,    textPaint  )  textPaint.isFakeBoldText = false  textPaint.textSize = greedSize  canvas.drawText(    greedTip,    centerX.toFloat(),    centerY.toFloat() - textPaint.textSize * 2,    textPaint  ) } private fun drawCircle(canvas: Canvas) {  //繪制外圍進度圓圈  canvas.drawArc(outerCircleRectf, 0f, 360f, false, outerNormalCirclePaint)  canvas.drawArc(outerCircleRectf, 90f, percent * 3.6f, false, outerCirclePaint)  canvas.drawCircle(    centerX.toFloat(),    centerY.toFloat(),    innerRadius,    bgCirclePaint  ) }}

以上是“Android如何自定義球型水波紋帶圓弧進度效果”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI