您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“Android如何實(shí)現(xiàn)球型水波紋帶圓弧進(jìn)度效果”,內(nèi)容簡(jiǎn)而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Android如何實(shí)現(xiàn)球型水波紋帶圓弧進(jìn)度效果”這篇文章吧。
需求
如下,實(shí)現(xiàn)一個(gè)圓形水波紋,帶進(jìn)度,兩層水波紋需要漸變顯示,且外圍有一個(gè)圓弧進(jìn)度。
思路
外圍圓弧進(jìn)度:可以通過(guò)canvas.drawArc()
實(shí)現(xiàn)。由于圓弧需要實(shí)現(xiàn)漸變,可以通過(guò)給畫(huà)筆設(shè)置shader(SweepGradient)
渲染,為了保證圓弧起始的顏色值始終一致,需要?jiǎng)討B(tài)調(diào)整shader的參數(shù)。具體參見(jiàn)
SweepGradient(centerX.toFloat(), centerY.toFloat(), circleColors[0], floatArrayOf(0f, value / 100f))
第四個(gè)參數(shù)需要根據(jù)當(dāng)前進(jìn)度填寫(xiě)對(duì)應(yīng)數(shù)據(jù)比例。不懂的同學(xué)可以自行百度查閱。
水波紋的實(shí)現(xiàn):直接使用貝塞爾曲線Path.quadTo()實(shí)現(xiàn),通過(guò)拉伸水平直線繪制波浪效果??梢酝ㄟ^(guò)控制拉伸點(diǎn)(waveAmplitude)距離水平線的高度,達(dá)到波浪高度的控制。至于波浪的移動(dòng),可以通過(guò)移動(dòng)平移水平線的起始位置來(lái)實(shí)現(xiàn),在使用動(dòng)畫(huà)循環(huán)即可,為了能夠穩(wěn)定的顯示,繪制波浪時(shí)需要嚴(yán)格繪制整數(shù)倍周期的波浪。
園形的實(shí)現(xiàn):繪制一個(gè)完整的圓形,然后通過(guò)Path.op()合并裁剪水波紋path。注意點(diǎn)就是Android6有個(gè)坑,使用該方法會(huì)有明顯的抖動(dòng),為了解決該問(wèn)題,我的做法是多畫(huà)一層圓弧以掩蓋此抖動(dòng)。
生命周期的控制:為了減少某些時(shí)刻CPU的損耗,通過(guò)控制變量自定義lifeDelegate(基于kotlin的代理模式實(shí)現(xiàn))來(lái)控制動(dòng)畫(huà)的開(kāi)始暫停。由于筆者使用的框架基于MVVM,所以代碼就沒(méi)有使用attrs控制屬性,這里就不做過(guò)多的修改了。
整體實(shí)現(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 //外圍圓圈的畫(huà)筆 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度開(kāi)啟漸變,由于線條頭部有個(gè)小圓圈會(huì)導(dǎo)致顯示差異,因此從88度開(kāi)始繪制 private val sweepMatrix by lazy { val matrix = Matrix() matrix.setRotate(88f, centerX.toFloat(), centerY.toFloat()) matrix } //進(jìn)度 0-100 var percent = 0 set(value) { field = value waveWaterLevelRatio = value / 100f //y = -4 * x2 + 4x拋物線計(jì)算振幅,水波紋振幅規(guī)律更加真實(shí) 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 //外圍圓圈的畫(huà)筆大小 private var outerStrokeWidth = 10f private var fAnimatedValue = 0f private var sAnimatedValue = 0f //動(dòng)畫(huà) 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è)置中間進(jìn)度文本的字體大小 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)前窗口銷(xiāo)毀時(shí),回收動(dòng)畫(huà)資源 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存在明顯抖動(dòng),因此多畫(huà)一圈圓環(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) { //繪制進(jìn)度文字 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) { //繪制外圍進(jìn)度圓圈 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如何實(shí)現(xiàn)球型水波紋帶圓弧進(jìn)度效果”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。