欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

利用Jetpack Compose繪制可愛的天氣動(dòng)畫

 更新時(shí)間:2022年01月19日 15:24:02   作者:ax2djmti  
Jetpack Compose是用于構(gòu)建原生Android UI的現(xiàn)代工具包。Jetpack Compose使用更少的代碼,強(qiáng)大的工具和直觀的Kotlin API,簡(jiǎn)化并加速了Android上的UI開發(fā)。本文將利用Jetpack Compose繪制可愛的天氣動(dòng)畫,感興趣的可以了解一下

1. 項(xiàng)目背景

最近參加了Compose挑戰(zhàn)賽的終極挑戰(zhàn),使用Compose完成了一個(gè)天氣app。之前幾輪挑戰(zhàn)也都有參與,每次都學(xué)到不少新東西。如今迎來(lái)最終挑戰(zhàn),希望能將這段時(shí)間的積累活學(xué)活用,做出更加成熟的作品。

項(xiàng)目挑戰(zhàn)

因?yàn)闆](méi)有美工協(xié)助,所以我考慮通過(guò)代碼實(shí)現(xiàn)app中的所有UI元素例如各種icon等,這樣的UI在任何分辨率下都不會(huì)失真,跟重要的是可以靈活地實(shí)現(xiàn)各種動(dòng)畫效果。

為了降低實(shí)現(xiàn)成本,我將app中的UI元素定義成偏卡通的風(fēng)格,可以更容易地通過(guò)代繪實(shí)現(xiàn):

上面的動(dòng)畫沒(méi)有使用gif、lottie或者其他靜態(tài)資源,所有圖形都是基于Compose代碼繪制的。

2. MyApp:CuteWeather

App界面比較簡(jiǎn)潔,采用單頁(yè)面呈現(xiàn)(挑戰(zhàn)賽要求),卡通風(fēng)格的天氣動(dòng)畫算是相對(duì)于同類app的特色:

項(xiàng)目地址:https://github.com/vitaviva/compose-weather

App界面構(gòu)成

App縱向劃分為幾個(gè)功能區(qū)域,每個(gè)區(qū)域都涉及到一些不同的Compose API的使用

涉及技術(shù)點(diǎn)較多,本文主要介紹如何使用Compose繪制自定義圖形、并基于這些圖形實(shí)現(xiàn)動(dòng)畫,其他內(nèi)容有機(jī)會(huì)再單獨(dú)介紹。

3. Compose自定義繪制

像常規(guī)的Android開發(fā)一樣,除了提供各種默認(rèn)的Composable控件以外,Compose也提供了Canvas用來(lái)繪制自定義UI。

其實(shí)Canvas相關(guān)API在各個(gè)平臺(tái)都大同小異,但在Compose上的使用有以下特點(diǎn):

  • 用聲明式的方式創(chuàng)建和使用Canvas
  • 通過(guò)DrawScope提供必要的state及各種APIs
  • API更簡(jiǎn)單易用

聲明式地創(chuàng)建和使用Canvas

Compose中,Canvas作為Composable,可以聲明式地添加到其他Composable中,并通過(guò)Modifier進(jìn)行配置

Canvas(modifier = Modifier.fillMaxSize()){ // this: DrawScope 
 //內(nèi)部進(jìn)行自定義繪制
}

傳統(tǒng)方式需要獲取Canvas句柄命令式的進(jìn)行繪制,而Canvas{...}通過(guò)狀態(tài)驅(qū)動(dòng)的方式在block內(nèi)執(zhí)行繪制邏輯、刷新UI。

強(qiáng)大的DrawScope

Canvas{...}內(nèi)部通過(guò)DrawScope提供必要的state用來(lái)獲取當(dāng)前繪制所需環(huán)境變量,例如我們最常用的size。DrawScope還提了各種常用的繪制API,例如drawLine

Canvas(modifier = Modifier.fillMaxSize()){
 //通過(guò)size獲取當(dāng)前canvas的width和height
    val canvasWidth = size.width
    val canvasHeight = size.height

 //繪制直線
    drawLine(
        start = Offset(x=canvasWidth, y = 0f),
        end = Offset(x = 0f, y = canvasHeight),
        color = Color.Blue,
        strokeWidth = 5F //設(shè)置直線寬度
    )
}

上面代碼繪制效果如下:

4.簡(jiǎn)單易用的API

傳統(tǒng)的Canvas API需要進(jìn)行Paint等配置;DrawScope提供的API更簡(jiǎn)單,使用更友好。

例如繪制一個(gè)圓,傳統(tǒng)的API是這樣:

public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) {
 //... 
}

DrawScope提供的API:

fun drawCircle(
    color: Color,
    radius: Float = size.minDimension / 2.0f,
    center: Offset = this.center,
    alpha: Float = 1.0f,
    style: DrawStyle = Fill,
    colorFilter: ColorFilter? = null,
    blendMode: BlendMode = DefaultBlendMode
) {...}

看起來(lái)參數(shù)變多了,但是其實(shí)已經(jīng)通過(guò)size等設(shè)置了合適的默認(rèn)值,同時(shí)省去了對(duì)Paint的創(chuàng)建和配置,使用起來(lái)更方便。

使用原生Canvas

目前DrawScope提供的API還不及原生Canvas豐富(比如不支持drawText等),當(dāng)不滿足使用需求時(shí),也可以直接使用原生Canvas對(duì)象進(jìn)行繪制

drawIntoCanvas { canvas ->
            //nativeCanvas是原生canvas對(duì)象,android平臺(tái)即android.graphics.Canvas
            val nativeCanvas  = canvas.nativeCanvas

        }

上面介紹了Compose Canvas的基本知識(shí),下面結(jié)合app中的具體示例看一下實(shí)際使用效果

首先,看一下雨水的繪制過(guò)程。

5. 雨天效果

雨天天氣的關(guān)鍵是如何繪制不斷下落的雨水

雨滴的繪制

我們先繪制構(gòu)成雨水的基本單元:雨滴

經(jīng)拆解后,雨水效果可由三組雨滴構(gòu)成,每一組雨滴分成上下兩端,這樣在運(yùn)動(dòng)時(shí)就可以形成接連不斷的雨水效果。我們使用drawLine繪制每一段黑線,設(shè)置適當(dāng)?shù)?code>stokeWidth,并通過(guò)cap設(shè)置端點(diǎn)的圓形效果:

@Composable
fun rainDrop() {

 Canvas(modifier) {

       val x: Float = size.width / 2 //x坐標(biāo):1/2的位置

        drawLine(
            Color.Black,
            Offset(x, line1y1), //line1 的起點(diǎn)
            Offset(x, line1y2), //line1 的終點(diǎn)
            strokeWidth = width, //設(shè)置寬度
            cap = StrokeCap.Round//頭部圓形
        )

  // line2同上
        drawLine(
            Color.Black,
            Offset(x, line2y1),
            Offset(x, line2y2),
            strokeWidth = width,
            cap = StrokeCap.Round
        )
    }
}

雨滴下落動(dòng)畫

完成基本圖形的繪制后,接下來(lái)為兩線段實(shí)現(xiàn)循環(huán)往復(fù)的位移動(dòng)畫,形成雨水的流動(dòng)效果。

以兩線段中間空隙為動(dòng)畫的錨點(diǎn),根據(jù)animationState設(shè)置其y軸位置,讓其從繪制區(qū)域的頂端移動(dòng)到低端(0 ~ size.hight),然后restart這個(gè)動(dòng)畫。

以錨點(diǎn)為基準(zhǔn)繪制上下兩線段,就可以行成接連不斷的雨滴效果了

代碼如下:

@Composable
fun rainDrop() {
 //循環(huán)播放的動(dòng)畫 ( 0f ~ 1f)
    val animateTween by rememberInfiniteTransition().animateFloat(
        initialValue = 0f,
        targetValue = 1f,
        animationSpec = infiniteRepeatable(
            tween(durationMillis, easing = LinearEasing),
            RepeatMode.Restart //start動(dòng)畫
        )
    )

    Canvas(modifier) {

        // scope : 繪制區(qū)域
        val width = size.width
        val x: Float = size.width / 2

   // width/2是strokCap的寬度,scopeHeight處預(yù)留strokCap寬度,讓雨滴移出時(shí)保持正圓,提高視覺(jué)效果
        val scopeHeight = size.height - width / 2 

        // space : 兩線段的間隙
        val space = size.height / 2.2f + width / 2 //間隙size
        val spacePos = scopeHeight * animateTween //錨點(diǎn)位置隨animationState變化
        val sy1 = spacePos - space / 2
        val sy2 = spacePos + space / 2

        // line length
        val lineHeight = scopeHeight - space

        // line1
        val line1y1 = max(0f, sy1 - lineHeight)
        val line1y2 = max(line1y1, sy1)

        // line2
        val line2y1 = min(sy2, scopeHeight)
        val line2y2 = min(line2y1 + lineHeight, scopeHeight)

        // draw
        drawLine(
            Color.Black,
            Offset(x, line1y1),
            Offset(x, line1y2),
            strokeWidth = width,
            colorFilter = ColorFilter.tint(
                Color.Black
            ),
            cap = StrokeCap.Round
        )

        drawLine(
            Color.Black,
            Offset(x, line2y1),
            Offset(x, line2y2),
            strokeWidth = width,
            colorFilter = ColorFilter.tint(
                Color.Black
            ),
            cap = StrokeCap.Round
        )
    }
}

6.Compose自定義布局

上面完成了單個(gè)雨滴的圖形和動(dòng)畫,接下來(lái)我們使用三個(gè)雨滴組成雨水的效果。

首先可以使用Row+Space的方式進(jìn)行組裝,但是這種方式缺少靈活性,僅通過(guò)Modifier很難準(zhǔn)確布局三個(gè)雨滴的相對(duì)位置。因此考慮轉(zhuǎn)而使用Compose的自定義布局,以提高靈活性和準(zhǔn)確性:

Layout(
    modifier = modifier.rotate(30f), //雨滴旋轉(zhuǎn)角度
    content = { // 定義子Composable
  Raindrop(modifier.fillMaxSize())
  Raindrop(modifier.fillMaxSize())
  Raindrop(modifier.fillMaxSize())
    }
) { measurables, constraints ->
    // List of measured children
    val placeables = measurables.mapIndexed { index, measurable ->
        // Measure each children
        val height = when (index) { //讓三個(gè)雨滴的height不同,增加錯(cuò)落感
            0 -> constraints.maxHeight * 0.8f
            1 -> constraints.maxHeight * 0.9f
            2 -> constraints.maxHeight * 0.6f
            else -> 0f
        }
        measurable.measure(
            constraints.copy(
                minWidth = 0,
                minHeight = 0,
                maxWidth = constraints.maxWidth / 10, // raindrop width
                maxHeight = height.toInt(),
            )
        )
    }

    // Set the size of the layout as big as it can
    layout(constraints.maxWidth, constraints.maxHeight) {
        var xPosition = constraints.maxWidth / ((placeables.size + 1) * 2)

        // Place children in the parent layout
        placeables.forEachIndexed { index, placeable ->
            // Position item on the screen
            placeable.place(x = xPosition, y = 0)

            // Record the y co-ord placed up to
            xPosition += (constraints.maxWidth / ((placeables.size + 1) * 0.8f)).roundToInt()
        }
    }
}

Compose中,可以通過(guò)Layout{...}對(duì)Composable進(jìn)行自定義布局,content{...}中定義參與布局的子Composable。

跟傳統(tǒng)Android視圖一樣,自定義布局需要先后經(jīng)歷measure、layout兩步。

measrue:measurables返回所有待測(cè)量的子Composable,constraints類似于MeasureSpec,封裝父容器對(duì)子元素的布局約束。measurable.measure()中對(duì)子元素進(jìn)行測(cè)量

layout:placeables返回測(cè)量后的子元素,依次調(diào)用placeable.place()對(duì)雨滴進(jìn)行布局,通過(guò)xPosition預(yù)留雨滴在x軸的間隔

經(jīng)過(guò)layout之后,通過(guò) modifier.rotate(30f) 對(duì)Composable進(jìn)行旋轉(zhuǎn),完成最終效果:

7.. 雪天效果

雪天效果的關(guān)鍵在于雪花的飄落。

雪花的繪制

雪花的繪制非常簡(jiǎn)單,用一個(gè)圓圈代表一個(gè)雪花

Canvas(modifier) {

 val radius = size / 2

 drawCircle( //白色填充
  color = Color.White,
  radius = radius,
  style = FILL
 )

  drawCircle(// 黑色邊框
   color = Color.Black,
     radius = radius,
  style = Stroke(width = radius * 0.5f)
 )
}

雪花飄落動(dòng)畫

雪花飄落的過(guò)程相對(duì)于雨滴墜落要復(fù)雜一些,由三個(gè)動(dòng)畫組成:

  • 下降:通過(guò)改變y軸位置實(shí)現(xiàn) (0f ~ 2.5f)
  • 左右飄移:通過(guò)該表x軸的offset實(shí)現(xiàn) (-1f ~ 1f)
  • 逐漸消失:通過(guò)改變alpha實(shí)現(xiàn)(1f ~ 0f)

借助InfiniteTransition同步控制多個(gè)動(dòng)畫,代碼如下:

@Composable
private fun Snowdrop(
 modifier: Modifier = Modifier,
 durationMillis: Int = 1000 // 雪花飄落動(dòng)畫的druation
) {

 //循環(huán)播放的Transition
    val transition = rememberInfiniteTransition()

 //1\. 下降動(dòng)畫:restart動(dòng)畫
    val animateY by transition.animateFloat(
        initialValue = 0f,
        targetValue = 2.5f,
        animationSpec = infiniteRepeatable(
            tween(durationMillis, easing = LinearEasing),
            RepeatMode.Restart
        )
    )

 //2\. 左右飄移:reverse動(dòng)畫
    val animateX by transition.animateFloat(
        initialValue = -1f,
        targetValue = 1f,
        animationSpec = infiniteRepeatable(
            tween(durationMillis / 3, easing = LinearEasing),
            RepeatMode.Reverse
        )
    )

 //3\. alpha值:restart動(dòng)畫,以0f結(jié)束
    val animateAlpha by transition.animateFloat(
        initialValue = 1f,
        targetValue = 0f,
        animationSpec = infiniteRepeatable(
            tween(durationMillis, easing = FastOutSlowInEasing),
        )
    )

    Canvas(modifier) {

        val radius = size.width / 2

  // 圓心位置隨AnimationState改變,實(shí)現(xiàn)雪花飄落的效果
        val _center = center.copy(
            x = center.x + center.x * animateX,
            y = center.y + center.y * animateY
        )

        drawCircle(
            color = Color.White.copy(alpha = animateAlpha),//alpha值的變化實(shí)現(xiàn)雪花消失效果
            center = _center,
            radius = radius,
        )

        drawCircle(
            color = Color.Black.copy(alpha = animateAlpha),
            center = _center,
            radius = radius,
            style = Stroke(width = radius * 0.5f)
        )
    }
}

animateYtargetValue設(shè)為2.5f,讓雪花的運(yùn)動(dòng)軌跡更長(zhǎng),看起來(lái)更加真實(shí)

雪花的自定義布局

像雨滴一樣,對(duì)雪花也使用Layout自定義布局

@Composable
fun Snow(
    modifier: Modifier = Modifier,
    animate: Boolean = false,
) {

    Layout(
        modifier = modifier,
        content = {
         //擺放三個(gè)雪花,分別設(shè)置不同duration,增加隨機(jī)性
            Snowdrop( modifier.fillMaxSize(), 2200)
            Snowdrop( modifier.fillMaxSize(), 1600)
            Snowdrop( modifier.fillMaxSize(), 1800)
        }
    ) { measurables, constraints ->
        val placeables = measurables.mapIndexed { index, measurable ->
            val height = when (index) {
             // 雪花的height不同,也是為了增加隨機(jī)性
                0 -> constraints.maxHeight * 0.6f
                1 -> constraints.maxHeight * 1.0f
                2 -> constraints.maxHeight * 0.7f
                else -> 0f
            }
            measurable.measure(
                constraints.copy(
                    minWidth = 0,
                    minHeight = 0,
                    maxWidth = constraints.maxWidth / 5, // snowdrop width
                    maxHeight = height.roundToInt(),
                )
            )
        }

        layout(constraints.maxWidth, constraints.maxHeight) {
            var xPosition = constraints.maxWidth / ((placeables.size + 1))

            placeables.forEachIndexed { index, placeable ->
                placeable.place(x = xPosition, y = -(constraints.maxHeight * 0.2).roundToInt())

                xPosition += (constraints.maxWidth / ((placeables.size + 1) * 0.9f)).roundToInt()
            }
        }
    }
}

最終效果如下:

8. 晴天效果

通過(guò)一個(gè)旋轉(zhuǎn)的太陽(yáng)代表晴天效果

太陽(yáng)的繪制

太陽(yáng)的圖形由中間的圓形和圍繞圓環(huán)的等分豎線組成。

@Composable
fun Sun(modifier: Modifier = Modifier) {

    Canvas(modifier) {

        val radius = size.width / 6
        val stroke = size.width / 20

        // draw circle
        drawCircle(
            color = Color.Black,
            radius = radius + stroke / 2,
            style = Stroke(width = stroke),
        )
        drawCircle(
            color = Color.White,
            radius = radius,
            style = Fill,
        )

        // draw line

        val lineLength = radius * 0.2f
        val lineOffset = radius * 1.8f
        (0..7).forEach { i ->

            val radians = Math.toRadians(i * 45.0)

            val offsetX = lineOffset * cos(radians).toFloat()
            val offsetY = lineOffset * sin(radians).toFloat()

            val x1 = size.width / 2 + offsetX
            val x2 = x1 + lineLength * cos(radians).toFloat()

            val y1 = size.height / 2 + offsetY
            val y2 = y1 + lineLength * sin(radians).toFloat()

            drawLine(
                color = Color.Black,
                start = Offset(x1, y1),
                end = Offset(x2, y2),
                strokeWidth = stroke,
                cap = StrokeCap.Round
            )
        }
    }
}

均分360度,每間隔45度畫一條豎線,cos計(jì)算x軸坐標(biāo),sin計(jì)算y軸坐標(biāo)。

太陽(yáng)的旋轉(zhuǎn)

太陽(yáng)的旋轉(zhuǎn)動(dòng)畫很簡(jiǎn)單,通過(guò)Modifier.rotate不斷轉(zhuǎn)動(dòng)Canvas即可。

@Composable
fun Sun(modifier: Modifier = Modifier) {

 //循環(huán)動(dòng)畫
    val animateTween by rememberInfiniteTransition().animateFloat(
        initialValue = 0f,
        targetValue = 360f,
        animationSpec = infiniteRepeatable(tween(5000), RepeatMode.Restart)
    )

    Canvas(modifier.rotate(animateTween)) {// 旋轉(zhuǎn)動(dòng)畫

        val radius = size.width / 6
        val stroke = size.width / 20
        val centerOffset = Offset(size.width / 30, size.width / 30) //圓心偏移量

        // draw circle
        drawCircle(
            color = Color.Black,
            radius = radius + stroke / 2,
            style = Stroke(width = stroke),
            center = center + centerOffset //圓心偏移
        )

        //...略
    }
}

此外,DrawScope也提供了rotate的API,也可以實(shí)現(xiàn)旋轉(zhuǎn)效果。

最后我們給太陽(yáng)的圓心增加一個(gè)偏移量,讓轉(zhuǎn)動(dòng)更加活潑:

9. 動(dòng)畫的組合、切換

上面分別實(shí)現(xiàn)了Rain、Snow、Sun等圖形,接下來(lái)使用這些元素組合成各種天氣效果。

將圖形組合成天氣

Compose的聲明式語(yǔ)法非常有利于UI的組合:

比如,多云轉(zhuǎn)陣雨,我們擺放Sun、Cloud、Rain等元素后,通過(guò)Modifier調(diào)整各自位置即可:

@Composable
fun CloudyRain(modifier: Modifier) {
 Box(modifier.size(200.dp)){
  Sun(Modifier.size(120.dp).offset(140.dp, 40.dp))
  Rain(Modifier.size(80.dp).offset(80.dp, 60.dp))
  Cloud(Modifier.align(Aligment.Center))
 }
}

讓動(dòng)畫切換更加自然

當(dāng)在多個(gè)天氣動(dòng)畫之間進(jìn)行切換時(shí),我們希望能實(shí)現(xiàn)更自然的過(guò)渡。實(shí)現(xiàn)思路是將組成天氣動(dòng)畫的各元素的Modifier信息變量化,然后通過(guò)Animation進(jìn)行改變state 假設(shè)所有的天氣都可以由Cloud、Sun、Rain組合而成,無(wú)非就是offset、size、alpha值的不同:

ComposeInfo
data class IconInfo(
    val size: Float = 1f, 
    val offset: Offset = Offset(0f, 0f),
    val alpha: Float = 1f,
) 

//天氣組合信息,即Sun、Cloud、Rain的位置信息
data class ComposeInfo(
    val sun: IconInfo,
    val cloud: IconInfo,
    val rains: IconInfo,

) {
    operator fun times(float: Float): ComposeInfo =
        copy(
            sun = sun * float,
            cloud = cloud * float,
            rains = rains * float
        )

    operator fun minus(composeInfo: ComposeInfo): ComposeInfo =
        copy(
            sun = sun - composeInfo.sun,
            cloud = cloud - composeInfo.cloud,
            rains = rains - composeInfo.rains,
        )

    operator fun plus(composeInfo: ComposeInfo): ComposeInfo =
        copy(
            sun = sun + composeInfo.sun,
            cloud = cloud + composeInfo.cloud,
            rains = rains + composeInfo.rains,
        )
}

如上,ComposeInfo中持有各種元素的位置信息,運(yùn)算符重載使其可以在Animation中計(jì)算當(dāng)前最新值。

接下來(lái),使用ComposeInfo為不同天氣定義各元素的位置信息

//晴天
val SunnyComposeInfo = ComposeInfo(
    sun = IconInfo(1f),
    cloud = IconInfo(0.8f, Offset(-0.1f, 0.1f), 0f),
    rains = IconInfo(0.4f, Offset(0.225f, 0.3f), 0f),
)

//多云
val CloudyComposeInfo = ComposeInfo(
    sun = IconInfo(0.1f, Offset(0.75f, 0.2f), alpha = 0f),
    cloud = IconInfo(0.8f, Offset(0.1f, 0.1f)),
    rains = IconInfo(0.4f, Offset(0.225f, 0.3f), alpha = 0f),
)

//雨天
val RainComposeInfo = ComposeInfo(
    sun = IconInfo(0.1f, Offset(0.75f, 0.2f), alpha = 0f),
    cloud = IconInfo(0.8f, Offset(0.1f, 0.1f)),
    rains = IconInfo(0.4f, Offset(0.225f, 0.3f), alpha = 1f),
)

ComposedIcon

接著,定義ComposedIcon,根據(jù)ComposeInfo實(shí)現(xiàn)不同的天氣組合

@Composable
fun ComposedIcon(modifier: Modifier = Modifier, composeInfo: ComposeInfo) {

 //各元素的ComposeInfo
    val (sun, cloud, rains) = composeInfo

    Box(modifier) {

  //應(yīng)用ComposeInfo到Modifier
        val _modifier = remember(Unit) {
            { icon: IconInfo ->
                Modifier
                    .offset( icon.size * icon.offset.x, icon.size * icon.offset.y )
                    .size(icon.size)
                    .alpha(icon.alpha)
            }
        }

        Sun(_modifier(sun))
        Rains(_modifier(rains))
        AnimatableCloud(_modifier(cloud))
    }
}

ComposedWeather

最后,定義ComposedWeather記錄當(dāng)前ComposedIcon,并在其發(fā)生更新時(shí)使用動(dòng)畫進(jìn)行過(guò)度:

@Composable
fun ComposedWeather(modifier: Modifier, composedIcon: ComposedIcon) {

    val (cur, setCur) = remember { mutableStateOf(composedIcon) }
    var trigger by remember { mutableStateOf(0f) }

    DisposableEffect(composedIcon) {
        trigger = 1f
        onDispose { }
    }

 //創(chuàng)建動(dòng)畫(0f ~ 1f),用于更新ComposeInfo
    val animateFloat by animateFloatAsState(
        targetValue = trigger,
        animationSpec = tween(1000)
    ) {
     //當(dāng)動(dòng)畫結(jié)束時(shí),更新ComposeWeather到最新state
        setCur(composedIcon)
        trigger = 0f
    }

 //根據(jù)AnimationState計(jì)算當(dāng)前ComposeInfo
    val composeInfo = remember(animateFloat) {
        cur.composedIcon + (weatherIcon.composedIcon - cur.composedIcon) * animateFloat
    }

以上就是利用Jetpack Compose繪制可愛的天氣動(dòng)畫的詳細(xì)內(nèi)容,更多關(guān)于Jetpack Compose繪制動(dòng)畫的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論