日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

自定义一个倒计时控件

發(fā)布時間:2024/1/1 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 自定义一个倒计时控件 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

主要需求

  • 能進行倒計時
  • 可以隨時取消、終止倒計時
  • 流暢跳動
  • 先看實現(xiàn)效果

    這是一個倒計時5s的動畫,可能錄制幀數(shù)導致沒有完全顯示,大于1秒時每秒顯示,小于1秒時顯示小數(shù),帶加速度先快后慢跳動

    實現(xiàn)思路

  • 繼承View來實現(xiàn)我們的控件
  • 重寫OnDraw方法來繪制進度條
  • 每秒跳動一次大進度,通過Handle定時觸發(fā),然后這一秒內(nèi),通過動畫控制進度條流動
  • 圓弧進度條主要由一個大圓+百分比圓弧構成,大圓使用描邊,設置大的描邊寬度實現(xiàn)圓環(huán)效果
  • 然后用圓弧覆蓋在上面實現(xiàn)進度效果
  • 因為圓的圓心角是360° 所以把100%進度拆成360分,每1%繪制3.6°圓弧即可
  • 實現(xiàn)繪制固定進度

    class CounterDownView : View {//region 繪圖參數(shù)private lateinit var circlePaint: Paintprivate lateinit var textPaint: Paintprivate var ringWidthDp = 12fprivate var ringBackgroundColor = Color.GRAYprivate var ringFillColor = Color.GREEN//endregion//region 定時參數(shù)private var targetTime = 5000Lprivate var currentTime = 1000Lprivate val interval = 1000private val handle: Handler by lazy { Handler(Looper.myLooper()!!) }var progressListener: ProgressListener? = null//endregion@JvmOverloadsconstructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : super(context,attrs,defStyleAttr) {ringWidthDp = dip2px(12f)circlePaint = Paint().apply {isAntiAlias = true //抗鋸齒isDither = true //防抖動strokeWidth = ringWidthDpshader = null}textPaint = Paint().apply {isAntiAlias = true //抗鋸齒isDither = true //防抖動isFakeBoldText = truecolor = Color.parseColor("#00FF90")textSize = dip2px(25f)textAlign = Paint.Align.CENTERstrokeWidth = 0f}ringBackgroundColor = Color.WHITE}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)val center = this.width / 2fval radius = center - ringWidthDp / 2f - ringWidthDpdrawBackgroundCircle(canvas, center, radius)drawProgressCircle(canvas, center, radius)drawShowText(canvas, center)}/*** 繪制背景圓環(huán)*/private fun drawBackgroundCircle(canvas: Canvas, center: Float, radius: Float) {with(circlePaint) {color = ringBackgroundColorstyle = Paint.Style.STROKE}canvas.drawCircle(center, center, radius, circlePaint)}/*** 繪制當前進度*/private fun drawProgressCircle(canvas: Canvas, center: Float, radius: Float) {val rectStartX = center - radiusval rectEndX = center + radius//圓弧的外接正方形,寬高相等val oval = RectF(rectStartX, rectStartX, rectEndX, rectEndX)circlePaint.color = ringFillColor//計算繪圖進度,轉化成圓弧的圓心角val ringAngle = 360f * currentTime / targetTime//繪制弧canvas.drawArc(oval, -90f, ringAngle, false, circlePaint)}private fun drawShowText(canvas: Canvas, center: Float) {var showText = ""val diff = (targetTime - currentTime) / 1000.0showText = if (diff < 1) { //小于1時顯示小數(shù)String.format("%1.1f", diff)} else {diff.toInt().toString()}//獲取文字邊框val textBound = Rect()textPaint.getTextBounds(showText, 0, showText.length, textBound)//計算文字基線高度,保證垂直居中val fontMetrics = textPaint.fontMetricsIntval baseLine = center + (fontMetrics.bottom - fontMetrics.top) / 2f - fontMetrics.bottomcanvas.drawText(showText, center, baseLine, textPaint)}/*** 啟動倒計時*/fun start() {}/*** 暫停倒計時*/fun pause() {}/*** 恢復倒計時*/fun resume() {}/*** 停止倒計時*/fun stop() {}//倒計時跳動控制核心private val counterRunnable = Runnable {}interface ProgressListener {fun onTick(counterDownView: CounterDownView): Booleanfun onFinish()}fun dip2px(dipValue: Float): Float {val scale = Resources.getSystem().displayMetrics.densityreturn dipValue * scale + 0.5f}

    至此,我們已經(jīng)能繪制出一個20%進度進度條了

    補全倒計時動態(tài)設置跳動功能 完整代碼

    class CounterDownView : View {![default.gif](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/67fcde8cb8a844da8db92a3136eeb85c~tplv-k3u1fbpfcp-watermark.image?)//region 繪圖參數(shù)private lateinit var circlePaint: Paintprivate lateinit var textPaint: Paintprivate var ringWidthDp = 12fprivate var ringBackgroundColor = Color.GRAYprivate var ringFillColor = Color.GREENprivate var valueAnimation: ValueAnimator? = nullprivate var counterRunnable: Runnable? = null//endregion//region 定時參數(shù)private var targetTime = 5000Lprivate var currentTime = 1000Lprivate val interval = 1000Lprivate val counterHandler: Handler by lazy { Handler(Looper.myLooper()!!) }var progressListener: ProgressListener? = null//endregion@JvmOverloadsconstructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : super(context,attrs,defStyleAttr) {ringWidthDp = dip2px(12f)circlePaint = Paint().apply {isAntiAlias = true //抗鋸齒isDither = true //防抖動strokeWidth = ringWidthDpshader = null}textPaint = Paint().apply {isAntiAlias = true //抗鋸齒isDither = true //防抖動isFakeBoldText = truecolor = Color.parseColor("#00FF90")textSize = dip2px(25f)textAlign = Paint.Align.CENTERstrokeWidth = 0f}ringBackgroundColor = Color.WHITE}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)val center = this.width / 2fval radius = center - ringWidthDp / 2f - ringWidthDpdrawBackgroundCircle(canvas, center, radius)drawProgressCircle(canvas, center, radius)drawShowText(canvas, center)}/*** 繪制背景圓環(huán)*/private fun drawBackgroundCircle(canvas: Canvas, center: Float, radius: Float) {with(circlePaint) {color = ringBackgroundColorstyle = Paint.Style.STROKE}canvas.drawCircle(center, center, radius, circlePaint)}/*** 繪制當前進度*/private fun drawProgressCircle(canvas: Canvas, center: Float, radius: Float) {val rectStartX = center - radiusval rectEndX = center + radius//圓弧的外接正方形,寬高相等val oval = RectF(rectStartX, rectStartX, rectEndX, rectEndX)circlePaint.color = ringFillColor//計算繪圖進度,轉化成圓弧的圓心角val ringAngle = 360f * currentTime / targetTime//繪制弧canvas.drawArc(oval, -90f, ringAngle, false, circlePaint)}private fun drawShowText(canvas: Canvas, center: Float) {var showText = ""val diff = (targetTime - currentTime) / 1000.0showText = if (diff < 1) { //小于1時顯示小數(shù)String.format("%1.1f", diff)} else {diff.toInt().toString()}//獲取文字邊框val textBound = Rect()textPaint.getTextBounds(showText, 0, showText.length, textBound)//計算文字基線高度,保證垂直居中val fontMetrics = textPaint.fontMetricsIntval baseLine = center + (fontMetrics.bottom - fontMetrics.top) / 2f - fontMetrics.bottomcanvas.drawText(showText, center, baseLine, textPaint)}/*** 啟動倒計時*/fun start(targetTime: Long = 5000L) {stop()//重置當前已走時間為0currentTime = 0this.targetTime = targetTime//先重繪一次界面invalidate()resume()}/*** 暫停倒計時*/fun pause() {counterRunnable?.let {counterHandler.removeCallbacks(it)}}/*** 恢復倒計時*/fun resume() {counterRunnable?.let {counterHandler.removeCallbacks(it)}//倒計時跳動控制核心counterRunnable = object : Runnable {override fun run() {//到時間了,如果已有動畫未完成,則先強制讓他完成動畫valueAnimation?.end()//回調(diào)給使用方,觸發(fā)一次倒計時if (progressListener?.onTick(this@CounterDownView) == true) {//主動結束倒計時,并保留狀態(tài)return}//跳動一次時間為 interval ,從現(xiàn)在開始到下次跳秒前,我們使用值動畫完成,讓進度條平滑過渡valueAnimation =ValueAnimator.ofInt(this@CounterDownView.currentTime.toInt(),(this@CounterDownView.currentTime + interval).toInt())valueAnimation?.let { ani ->ani.addUpdateListener {this@CounterDownView.currentTime = it.animatedValue.toString().toLong()postInvalidate()}ani.addListener(object : AnimatorListenerAdapter() {override fun onAnimationEnd(animation: Animator?) {if (this@CounterDownView.currentTime >= targetTime) { //只要超過目標時間就主動提示完成progressListener?.onFinish()this@CounterDownView.currentTime = 0L}}})//設置動畫插值器ani.interpolator = DecelerateInterpolator()ani.duration = this@CounterDownView.interval //設置動畫執(zhí)行時間ani.start()}if ((this@CounterDownView.currentTime + this@CounterDownView.interval) >= this@CounterDownView.targetTime) {//結束倒計時return} else {counterHandler.postDelayed(this, this@CounterDownView.interval)}}}// 為了讓界面能顯示總時間,延遲600ms再開始倒計時,也可以不延遲直接開始counterRunnable?.let {counterHandler.postDelayed(it, 600)}}/*** 停止倒計時*/fun stop() {valueAnimation?.cancel()pause()valueAnimation = null}interface ProgressListener {fun onTick(counterDownView: CounterDownView): Booleanfun onFinish()}private fun dip2px(dipValue: Float): Float {val scale = Resources.getSystem().displayMetrics.densityreturn dipValue * scale + 0.5f} }

    關于動畫插值器修改

    上面例子使用的插值器是DecelerateInterpolator,我們還可以換成線性動畫LinearInterpolator

    關于插值器的用法這里就不詳細說明了

    總結

    以上是生活随笔為你收集整理的自定义一个倒计时控件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。