新闻动态

Dialog 按照顺序弹窗的优雅写法

发布日期:2022-01-02 08:28 | 文章来源:脚本之家

我为 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 : DrawType
 object DrawImage : DrawType
 data 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 view
 object : AbstractComposeView(context) {
 
  @Composable
  override 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 = dp
 while (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 -> bitmap
  else -> 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.height

  canvas.drawPath (wave.buildWavePath(
width = maxWidth,
height = maxHeight,
amplitude = size.height * amplitude,
progress = progress), forePaint
  )
 }
 
}
  }
 }

需要源码可以私信我,当天回复

到此这篇关于Dialog 按照顺序弹窗的文章就介绍到这了,更多相关Dialog 按照顺序弹窗内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

版权声明:本站文章来源标注为YINGSOO的内容版权均为本站所有,欢迎引用、转载,请保持原文完整并注明来源及原文链接。禁止复制或仿造本网站,禁止在非www.yingsoo.com所属的服务器上建立镜像,否则将依法追究法律责任。本站部分内容来源于网友推荐、互联网收集整理而来,仅供学习参考,不代表本站立场,如有内容涉嫌侵权,请联系alex-e#qq.com处理。

相关文章

实时开通

自选配置、实时开通

免备案

全球线路精选!

全天候客户服务

7x24全年不间断在线

专属顾问服务

1对1客户咨询顾问

在线
客服

在线客服:7*24小时在线

客服
热线

400-630-3752
7*24小时客服服务热线

关注
微信

关注官方微信
顶部