温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

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

发布时间:2021-09-24 15:15:07 来源:亿速云 阅读:102 作者:柒染 栏目:开发技术

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 中,我们让 WaveLoadingImage 以组合的方式使用,这样的 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
                        )
                    }
     
                }
            }
        }

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

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    AI