文章目录

  • 前情提要
  • 实现倒计时功能
  • 注意事项
  • 完整代码地址

前情提要

上一篇 Android Kotlin之Flow数据流 中介绍了协程Flow,我们知道Flow数据流可以按顺序发送多个值,一个倒计时功能刚好符合这种场景,本文就尝试使用Flow来实现一个倒计时功能。

上文中举过一个简单示例:

 flow { log("send hello")emit("hello") //发送数据log("send world")emit("world") //发送数据}.flowOn(Dispatchers.IO).onEmpty { log("onEmpty") }.onStart { log("onStart") }.onEach { log("onEach: $it") }.onCompletion { log("onCompletion") }.catch { exception -> exception.message?.let { log(it) } }.collect {//接收数据流log("collect: $it")}

执行结果:

2021-09-27 19:51:54.433 7240-7240/ E/TTT: onStart
2021-09-27 19:51:54.439 7240-7325/ E/TTT: send hello
2021-09-27 19:51:54.440 7240-7325/ E/TTT: send world2021-09-27 19:51:54.451 7240-7240/ E/TTT: onEach: hello
2021-09-27 19:51:54.451 7240-7240/ E/TTT: collect:hello
2021-09-27 19:51:54.452 7240-7240/ E/TTT: onEach: world
2021-09-27 19:51:54.452 7240-7240/ E/TTT: collect:world
2021-09-27 19:51:54.453 7240-7240/ E/TTT: onCompletion
  • onStart:上游flow{}开始发送数据之前执行
  • onCompletionflow数据流取消或者结束时执行
  • onEach:上游向下游发送数据之前调用,每一个上游数据发送后都会经过onEach()

使用上面几个操作符,将传入的数据在flow{}中每隔1s(通过delay(1000)实现)发射出去,下游对接收的数据进行处理,即是一个倒计时功能,如下:

    /*** 使用Flow实现一个倒计时功能*/private fun countDownByFlow(max: Int,scope: CoroutineScope,onTick: (Int) -> Unit,onFinish: (() -> Unit)? = null,): Job {return flow {for (num in max downTo 0) {emit(num)if (num != 0) delay(1000)}}.flowOn(Dispatchers.Main).onEach { onTick.invoke(it) }.onCompletion { cause -> if (cause == null) onFinish?.invoke() }.launchIn(scope) //保证在一个协程中执行}

实现倒计时功能

先上效果图:

主要代码逻辑:

class CountDownCircleView @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0,
) : View(context, attrs, defStyleAttr) {private val mPaint: Paint = Paint().apply {isAntiAlias = trueisDither = truestyle = Paint.Style.STROKEstrokeCap = Paint.Cap.ROUNDstrokeWidth = 8.dp2px().toFloat()color = Color.parseColor("#F7F9FA")}private val mCirclePaint: Paint = Paint().apply {isAntiAlias = trueisDither = truestyle = Paint.Style.FILLcolor = Color.WHITE}private val mTextPaint: TextPaint = TextPaint().apply {isAntiAlias = trueisDither = truecolor = Color.parseColor("#A1EA42")textAlign = Paint.Align.CENTER //绘制方向 居中绘制textSize = 50.sp2px().toFloat()typeface = Typeface.DEFAULT_BOLD}private val mRect = RectF()private var mCenterX: Float = 0fprivate var mCenterY: Float = 0fprivate var mRadius: Float = 0fprivate var mMinWH: Float = 0fprivate val colorArr = intArrayOf(Color.parseColor("#BAF900"), Color.parseColor("#84F000"))//设置渐变色private val mLinearShader =LinearGradient(0f, 0f, mMinWH, mMinWH, colorArr, null, Shader.TileMode.MIRROR)private var mMaxCount: Int = 0 //最大数private var mSweepAngle: Float = 360f //扫描过的度数private var mCountDown: Job? = nullprivate var mText = ""/*** 开始倒计数*/fun startCountDown(count: Int, finishFuc: () -> Unit) {if (context !is FragmentActivity) returnthis.mMaxCount = countmCountDown = countDownByFlow(mMaxCount, (context as FragmentActivity).lifecycleScope,onTick = {if (it == 0) mCountDown?.cancel()mText = it.toString()mSweepAngle = (it / mMaxCount.toFloat()) * 360invalidate()}, onFinish = {finishFuc.invoke()})}override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {mCenterX = (w / 2).toFloat()mCenterY = (h / 2).toFloat()mMinWH = min(w, h).toFloat()mRadius = (mMinWH - mPaint.strokeWidth) / 2//设置矩形范围val strokeHalf = mPaint.strokeWidth / 2mRect.set(strokeHalf, strokeHalf, mMinWH - strokeHalf, mMinWH - strokeHalf)}override fun onDraw(canvas: Canvas?) {canvas?.let {mPaint.shader = null//绘制白色背景圆canvas.drawCircle(mCenterX, mCenterY, mRadius, mCirclePaint)//绘制灰色背景圆canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaint)//绘制渐变色弧形 从12点方向开始绘制mPaint.shader = mLinearShadercanvas.drawArc(mRect, -90f, mSweepAngle, false, mPaint)//绘制中间倒计时数字//如果设置的 Align = LEFT,那么baseX = (mMinWH - mTextPaint.measureText(mText)) / 2val baseX = mCenterX// 计算Baseline绘制的Y坐标 ,计算方式:画布高度的一半 - 文字总高度的一半val baseY =(mCenterY - (mTextPaint.descent() + mTextPaint.ascent()) / 2).toInt()// 居中画一个文字canvas.drawText(mText, baseX, baseY.toFloat(), mTextPaint)}}/*** 使用Flow实现一个倒计时功能*/private fun countDownByFlow(max: Int,scope: CoroutineScope,onTick: (Int) -> Unit,onFinish: (() -> Unit)? = null,): Job {return flow {for (num in max downTo 0) {emit(num)if (num != 0) delay(1000)}}.flowOn(Dispatchers.Main).onEach { onTick.invoke(it) }.onCompletion { cause -> if (cause == null) onFinish?.invoke() }.launchIn(scope) //保证在一个协程中执行}override fun onDetachedFromWindow() {super.onDetachedFromWindow()mCountDown?.cancel()}}

Activity中:

mBtnStart.setOnClickListener {mCountDownView.startCountDown(10) {showToast("倒计时结束")}
}

注意事项

  • 这里实现的倒计时功能是基于协程Flow实现的,所以必须保证项目里是支持Kotlin协程的才能使用;
  • 如果未使用协程Flow,将这里的倒计时逻辑改成CountDownTimer或者Timer来实现即可。

完整代码地址

完整代码地址:Kotlin Flow实现一个倒计时功能

Android 基于Kotlin Flow实现一个倒计时功能相关推荐

  1. android欢迎页圆形倒计时,android 欢迎页圆形进度条倒计时功能

    常见app欢迎页圆形进度条倒计时功能,可设置显示文字,进度条颜色,宽度,倒计时时间,内圆颜色.设置进度条类型  顺数进度条(0-100)还是倒数进度条(100-0): 先上效果图: 下面介绍实现逻辑: ...

  2. Flutter StreamBuilder 实现的一个倒计时功能

    题记 -- 执剑天涯,从你的点滴积累开始,所及之处,必精益求精. Flutter是谷歌推出的最新的移动开发框架. [x1]微信公众号的每日提醒 随时随记 每日积累 随心而过 [x2]各种系列的视频教程 ...

  3. Android手摸手实现一个画板功能(一)——View的拖拽

    一.概述   从之前项目中抽取出来的一个"画板"功能模块,就是可以在一个空白布局上,添加不同的元素,实现自由组合,暂时没想到啥好名字,姑且叫它"画板"吧.    ...

  4. Android基于NSD实现网络服务发现功能

    一. 简介 网络服务发现:一般是指通过此功能,在局域网内来发现同样支持此功能的设备,并跟其他设备建立连接. Android 提供了一个网络服务发现(NSD),可让应用访问其他设备在本地网路上提供的服务 ...

  5. Android中kotlin Flow的资源浪费/ANR问题

    1.前提 一开始用Flow,主要用来替代LiveData,用来观察. 然后发现了StateFlow和SharedFlow这两个主要就是用来替代LiveData的. 两者区别: StateFlow: 1 ...

  6. Android Kotlin Flow 如何使用callbackflow

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/121840157 本文出自[赵彦军的博客] callbackFlow 原理 call ...

  7. QT-日期倒计时功能的小坑

    想用QT做一个倒计时功能 参考这篇博客 遇到小坑记录一下 正确用法 void ctrl::timeUpDate() {QDateTime time=QDateTime::currentDateTime ...

  8. android 10 数字健康,Android P 的「数字健康」功能,实际体验如何?

    Android P 的「数字健康」功能,实际体验如何? 2019-04-10 15:43:52 2点赞 5收藏 1评论 入手三星S10e也有一段时间,很多功能还没有细细体验,最近发现系统设置里有个「数 ...

  9. Android开发:基于Kotlin编写一个简易计算器

    目录 前言 Kotlin学习tips 界面绘制及控件绑定 UI界面绘制 控件绑定 Button点击事件 运算逻辑 整体逻辑 边界情况 输入展示 点击数字键 点击运算符键 点击"=" ...

最新文章

  1. VS2010 重命名文件:源文件名和目标文件名相同 的解决方案
  2. matplotlib中文乱码
  3. sqlserver2008r2升级到2012的问题
  4. 关于stm32启动文件的选择
  5. 如何进行.NET高效开发
  6. Android Studio 下使用git -- 个人,本地版本控制
  7. python实现一个简单的加法计算器_Python简易项目 加减计算器的实现
  8. 9206 1225 mybank系统 随堂笔记
  9. Ceph保证数据安全的机制
  10. tfs2015 生成与发布 配置
  11. 生成器 Generators
  12. CoffeeScript、TypeScript 、 JavaScript 之间
  13. 想法独特 上半年最具特色摄像头集锦
  14. 抓包分析数据帧中IP数据包的一些关键字段【南邮网课例题】
  15. Ubuntu批量转换图片格式
  16. PowerShell,AnkhSVN和Subversion
  17. mysql创建唯一非聚集索引_创建聚集索引、非聚集索引、唯一索引、唯一键约束...
  18. 超详细从零记录Hadoop2.7.3完全分布式集群部署过程
  19. 使用intel编译器编译WRF4.4
  20. 差分放大电路及动态分析

热门文章

  1. 漆学军:MT4平台量化交易故事
  2. 苦七学习群提问内容整理210729
  3. 计算机二级考试剩30秒提交,计算机二级考试倒计时4天,四个方法教你高效备战...
  4. 从MediaRecord录像中读取H264参数
  5. 360安卓_有解商城安卓版App上架360及百度手机助手
  6. 《淘宝网开店 拍摄 修图 设计 装修 实战150招》一一1.14 如何掌握拍摄角度
  7. Global Usings 和 Implicit Usings
  8. java打印线程堆栈_Java问题定位之Java线程堆栈分析
  9. 你看,你看,月亮的笑脸!
  10. Rey英文版主题-时尚与服装商城主题-WordPress响应式