联系客服:400-805-1963

Dialog按照顺序弹窗的优雅写法是怎样的
更新:HHH   时间:2023-1-7


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 : DrawType
        object DrawImage : DrawType
        data class DrawColor(val color: Color) : DrawType
    }

    如上,DrawType 有三种类型:

    • None: 不进行绘制

    • DrawColor:使用单一颜色绘制

    • DrawImage:按照原样绘制

    以下面这个 Image 为例, 体会一下不同 DrawType 的组合效果

    indexbackDrawTypeforeDrawType说明
    1DrawImageDrawImage背景灰度,前景原图
    2DrawColor(Color.LightGray)DrawImage背景单色,前景原图
    3DrawColor(Color.LightGray)DrawColor(Color.Cyan)背景单色,前景单色
    4NoneDrawColor(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
                        )
                    }
     
                }
            }
        }

    看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注天达云行业资讯频道,感谢您对天达云的支持。

    返回开发技术教程...