Dialog 按照顺序弹窗
我为 Compose 写了一个波浪效果的进度加载库,API 的设计上符合 Compose 的开发规范,使用非常简便。
1. 使用方式
在 root 的 build.gradle
中引入 jitpack
,
allprojects {repositories {...maven { url 'https://jitpack.io' }}
}
在 module 的 build.gradle
中引入 ComposeWaveLoading
的最新版本
dependencies {implementation 'com.github.vitaviva:ComposeWaveLoading:$latest_version'
}
2. API 设计思想
Box {WaveLoading (progress = 0.5f // 0f ~ 1f) {Image(painter = painterResource(id = R.drawable.logo_tiktok),contentDescription = "")}
}
传统的 UI 开发方式中,设计这样一个波浪控件,一般会使用自定义 View 并将 Image 等作为属性传入。 而在 Compose 中,我们让 WaveLoading
和 Image
以组合的方式使用,这样的 API 更加灵活,WaveLoding
的内部可以是 Image
,也可以是 Text
亦或是其他 Composable
。波浪动画不拘泥于某一特定 Composable, 任何 Composable 都可以以波浪动画的形式展现, 通过 Composable 的组合使用,扩大了 “能力” 的覆盖范围。
3. API 参数介绍
@Composable
fun WaveLoading(modifier: Modifier = Modifier,foreDrawType: DrawType = DrawType.DrawImage,backDrawType: DrawType = rememberDrawColor(color = Color.LightGray),@FloatRange(from = 0.0, to = 1.0) progress: Float = 0f,@FloatRange(from = 0.0, to = 1.0) amplitude: Float = defaultAmlitude,@FloatRange(from = 0.0, to = 1.0) velocity: Float = defaultVelocity,content: @Composable BoxScope.() -> Unit
) { ... }
参数说明如下:
参数 | 说明 |
---|---|
progress | 加载进度 |
foreDrawType |
波浪图的绘制类型: DrawColor 或者 DrawImage
|
backDrawType | 波浪图的背景绘制 |
amplitude | 波浪的振幅, 0f ~ 1f 表示振幅在整个绘制区域的占比 |
velocity | 波浪移动的速度 |
content | 子Composalble |
接下来重点介绍一下 DrawType
。
DrawType
波浪的进度体现在前景(foreDrawType)和后景(backDrawType)的视觉差,我们可以为前景后景分别指定不同的 DrawType 改变波浪的样式。
sealed interface DrawType {object None : DrawTypeobject DrawImage : DrawTypedata class DrawColor(val color: Color) : DrawType
}
如上,DrawType 有三种类型:
- None: 不进行绘制
- DrawColor:使用单一颜色绘制
- DrawImage:按照原样绘制
以下面这个 Image
为例, 体会一下不同 DrawType 的组合效果
index | backDrawType | foreDrawType | 说明 |
---|---|---|---|
1 | DrawImage | DrawImage | 背景灰度,前景原图 |
2 | DrawColor(Color.LightGray) | DrawImage | 背景单色,前景原图 |
3 | DrawColor(Color.LightGray) | DrawColor(Color.Cyan) | 背景单色,前景单色 |
4 | None | DrawColor(Color.Cyan) | 无背景,前景单色 |
注意 backDrawType 设置为 DrawImage 时,会显示为灰度图。
4. 原理浅析
简单介绍一下实现原理。为了便于理解,代码经过简化处理,完整代码可以在 github 查看
这个库的关键是可以将 WaveLoading {...}
内容取出,加以波浪动画的形式显示。所以需要将子 Composalbe 转成 Bitmap 进行后续处理。
4.1 获取 Bitmap
我在 Compose 中没找到获取位图的办法,所以用了一个 trick 的方式, 通过 Compose 与 Android 原生视图良好的互操作性,先将子 Composalbe 显示在 AndroidView
中,然后通过 native 的方式获取 Bitmap:
@Composable
fun WaveLoading (...)
{Box {var _bitmap by remember {mutableStateOf(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565))}AndroidView(factory = { context ->// Creates custom viewobject : AbstractComposeView(context) {@Composableoverride fun Content() {Box(Modifier.wrapContentSize(){content()}}override fun dispatchDraw(canvas: Canvas?) {val bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)val canvas2 = Canvas(source)super.dispatchDraw(canvas2)_bitmap = bmp}}})WaveLoadingInternal(bitmap = _bitmap)}
}
AndroidView
是一个可以绘制 Composable 的原生控件,我们将 WaveLoading 的子 Composable 放在其 Content
中,然后在 dispatchDraw
中绘制时,将内容绘制到我们准备好的 Bitmap 中。
4.2 绘制波浪线
我们基于 Compose 的 Canvas 绘制波浪线,波浪线通过 Path
承载 定义 WaveAnim
用来进行波浪线的绘制
internal data class WaveAnim(val duration: Int,val offsetX: Float,val offsetY: Float,val scaleX: Float,val scaleY: Float,
) {private val _path = Path()//绘制波浪线internal fun buildWavePath(dp: Float,width: Float,height: Float,amplitude: Float,progress: Float): Path {var wave = (scaleY * amplitude).roundToInt() //计算拉伸之后的波幅_path.reset()_path.moveTo(0f, height)_path.lineTo(0f, height * (1 - progress))// 通过正弦曲线绘制波浪if (wave > 0) {var x = dpwhile (x < width) {_path.lineTo(x,height * (1 - progress) - wave / 2f * Math.sin(4.0 * Math.PI * x / width).toFloat())x += dp}}_path.lineTo(width, height * (1 - progress))_path.lineTo(width, height)_path.close()return _path}}
如上,波浪线 Path 通过正弦函数绘制。
4.3 波浪填充
有了 Path ,我们还需要填充内容。填充的内容前文已经介绍过,或者是 DrawColor
或者 DrawImage
。 绘制 Path 需要定义 Paint
val forePaint = remember(foreDrawType, bitmap) {Paint().apply {shader = BitmapShader(when (foreDrawType) {is DrawType.DrawColor -> bitmap.toColor(foreDrawType.color)is DrawType.DrawImage -> bitmapelse -> alphaBitmap},Shader.TileMode.CLAMP,Shader.TileMode.CLAMP)}}
Paint 使用 Shader 着色器绘制 Bitmap, 当 DrawType 只绘制单色时, 对位图做单值处理:
/*** 位图单色化*/
fun Bitmap.toColor(color: androidx.compose.ui.graphics.Color): Bitmap {val bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)val oldPx = IntArray(width * height) //用来存储原图每个像素点的颜色信息getPixels(oldPx, 0, width, 0, 0, width, height) //获取原图中的像素信息val newPx = oldPx.map {color.copy(Color.alpha(it) / 255f).toArgb()}.toTypedArray().toIntArray()bmp.setPixels(newPx, 0, width, 0, 0, width, height) //将处理后的像素信息赋给新图return bmp
}
4.4 波浪动画
最后通过 Compose 动画让波浪动起来
val transition = rememberInfiniteTransition()val waves = remember(Unit) {listOf(WaveAnim(waveDuration, 0f, 0f, scaleX, scaleY),WaveAnim((waveDuration * 0.75f).roundToInt(), 0f, 0f, scaleX, scaleY),WaveAnim((waveDuration * 0.5f).roundToInt(), 0f, 0f, scaleX, scaleY))}val animates : List<State<Float>> = waves.map { transition.animateOf(duration = it.duration) }
为了让波浪更有层次感,我们定义三个 WaveAnim
以 Set 的形式做动画
最后,配合 WaveAnim 将波浪的 Path 绘制到 Canvas 即可
Canvas{drawIntoCanvas { canvas ->//绘制后景canvas.drawRect(0f, 0f, size.width, size.height, backPaint)//绘制前景waves.forEachIndexed { index, wave ->canvas.withSave {val maxWidth = 2 * scaleX * size.width / velocity.coerceAtLeast(0.1f)val maxHeight = scaleY * size.heightcanvas.drawPath (wave.buildWavePath(width = maxWidth,height = maxHeight,amplitude = size.height * amplitude,progress = progress), forePaint)}}}}
需要源码可以私信我,当天回复
Dialog 按照顺序弹窗相关推荐
- Android弹出自定义Dialog,android自定义Dialog实现底部弹窗
android自定义Dialog实现底部弹窗 拿到这个需求,很多人都是直接想用popWindow 实现,但是这样的效果我们完全可以根据系统的Dialog 自定义一个. AlertDialog.Buil ...
- art.dialog重设弹窗大小和刷新位置
var adialog=parent.art.dialog.get('payapplydialog'); adialog.size(720,430); var obj=adialog.size(720 ...
- dialog对话框初始化 mfc_MFC中Dialog初始化顺序
原文:http://blog.sina.com.cn/s/blog_472a9f0c0101ax3q.html 有需要的可以点击进去查看. 1.模式对话框的创建过程: 1)DoModal() 重载 ...
- Android优雅实现弹窗优先级管理
前言 在日常的android开发中,我们或多或少都有做过应用内的一些弹窗,比如在应用的某些页面弹窗展示广告,弹窗通知消息等.你的app中使用弹窗是否比较频繁?你是否厌烦了每次敲击一大堆代码就为了展示一 ...
- 开发kendo-ui弹窗组件
摘要: kendo-ui中只是提供了windwo插件,并没有提供页内弹窗插件.现在分享项目中自己定制的基于window组件的弹窗插件,如果你的项目也是用的kendo-ui,只需要将组件代码引到项目中即 ...
- 小程序dialog ,警告:未找到 van-dialog 节点,请确认 selector 及 context 是否正确
文档: Dialog.alert({message: '弹窗内容' }).then(() => {// on close }); 修改: 需要:content: this Dialog.aler ...
- easyUI datagrid editor扩展dialog
easyUI datagrid简单使用:着重两点1.editor对象的click事件:2.将dialog窗体内的值填写到当前正编辑的单元格内 1 <!DOCTYPE html> 2 < ...
- Android 天气APP(十三)仿微信弹窗(右上角加号点击弹窗效果)、自定义背景图片、UI优化调整
上一篇:Android 天气APP(十二)空气质量.UI优化调整 天气预报详情,逐小时预报详情 新版------------------- 一.适配器点击监听 二.页面实现 三.天气预报详情弹窗 四. ...
- Android底部弹窗的正确打开方式
阅读完本文后,你可以有以下收获 利用PopupWindow实现底部弹窗 PopupWindow实现底部弹窗时的缺点 解决利用PopupWindow实现底部弹窗,无法覆盖状态栏的问题 利用dialog实 ...
最新文章
- 《麻省理工科技评论》发布2019年全球十大突破性技术!
- ELF 文件 动态链接 - 地址无关代码(GOT)
- Part1_4 python函数、文件操作、异常处理
- c语言平滑raw图像(取平均值法)
- 使用SQLite3存储和读取数据
- SAP 产品一脉相承的 UI 增强思路,在 SAP Commerce Cloud(电商云) UI 增强实现中的体现
- sqlite数据库的char,varchar,text,nchar,nvarchar,ntext的区别
- Copying to tmp table Problem Solving
- java怎么知道上传文件是否成功_文件包含漏洞之——tomcat CVE-2020-1938漏洞复现
- ajax传值controller怎么写,关于ajax请求Controller传值问题详细记录
- Cannot connect to the database. –Error connecting to database.
- 百灵欧拓O2O移动广告平台
- 周易全文&白話翻譯(上)
- Ubuntu20.04 虚拟机 联网
- 2022年黑龙江最新建筑八大员(标准员)模拟考试试题及答案
- Windows驱动之IRP PENDING
- Python刷点击率,下载量代码
- 栈解旋unwinding
- C语言练习题:厘米换算英尺英寸
- 计算方法(四):插值与拟合
热门文章
- Windows学习总结
- The 36th ACM/ICPC Asia Regional Dalian Site 1006 Dave
- matlab最小二乘法拟合图旋转,【Matlab】—{最小二乘法拟合一阶线性拟合传感器实验}...
- 关于Android 版本向下兼容
- java 向后兼容性_关于java:JDK“向上”还是“向后”兼容?
- 啃完阿里这份高并发编程核心笔记,反手涨了5K
- 【LeetCode】332. 重新安排行程
- animate调整动画持续时间 修改动画持续时间 修改动画延迟时间
- 幽默及顿悟的哲理故事
- golang使用excelize导出Excel表格数据