0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

詳解Jetpack Compose布局流程

谷歌開發(fā)者 ? 來源:AndroidPub ? 2025-02-05 13:38 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

本文作者 / Android 谷歌開發(fā)者專家王鵬

前言 - 從 Compose 生命周期說起

ba236dbc-da36-11ef-9310-92fbcf53809c.png

Compose 繪制生命周期為三個(gè)階段:

Composition/組合: Composable 源碼經(jīng)過運(yùn)行后生成 LayoutNode 的節(jié)點(diǎn)樹,這棵樹被稱為 Composition。

Layout/布局: 對(duì)節(jié)點(diǎn)樹深度遍歷測量子節(jié)點(diǎn)的尺寸,并將其在父容器內(nèi)擺放到合適的位置。

Drawing/繪制: 基于布局后拿到的尺寸和位置信息,繪制上屏。

我們與 Android 經(jīng)典視圖系統(tǒng)的生命周期 (Measure,Layout,Drawing) 做一個(gè)對(duì)比: 組合是 Compose 的特有階段,是其能夠通過函數(shù)調(diào)用實(shí)現(xiàn)聲明式 UI 的核心,想要深入理解 Compose 第一課就是理解這個(gè)過程。

繪制階段與傳統(tǒng)視圖大同小異,都是通過 Android Cavas API,底層調(diào)用 skia 實(shí)現(xiàn)。

本文討論的重點(diǎn)是布局階段。Compose 的 Layout 把 Measure 也囊括了進(jìn)來,相對(duì)于 Android View 有相似性,但也有其獨(dú)有的特點(diǎn)和優(yōu)勢,接下來我們進(jìn)入正題。

Compose 布局過程三步走

Compose 布局包括三個(gè)階段,從當(dāng)前 Node 出發(fā),需要依次經(jīng)歷:

Measure children: 深度遍歷子節(jié)點(diǎn),并測量它們的尺寸

Decide own size: 根據(jù)收集到的子節(jié)點(diǎn)尺寸,決定當(dāng)前節(jié)點(diǎn)自己的尺寸

Place children: 將子節(jié)點(diǎn)擺放到合理的相對(duì)位置

wKgZO2ei-d-ASRvkAACju1W21oo095.png

上面代碼描述了一個(gè)卡片的布局,下面以這個(gè)布局的節(jié)點(diǎn)樹為例,看一下布局流程。

ba53930c-da36-11ef-9310-92fbcf53809c.png

Step1: 從 Row 開始發(fā)起測量,遵循三步走第一步,深度遍歷測量其子節(jié)點(diǎn) Image 和 Column

Step2&3: Image 發(fā)起測量,因?yàn)闆]有子節(jié)點(diǎn)需要測量了,所以只需要計(jì)算自己的尺寸,也因?yàn)闆]有子節(jié)點(diǎn)需要擺放,空實(shí)現(xiàn)完成 place 即可

Step4: Column 發(fā)起測量,因其有子節(jié)點(diǎn),繼續(xù)深度遍歷

Step5&6: 測量 Text,因?yàn)橐粋€(gè)葉子節(jié)點(diǎn),立即完成自己的 Size 和 Place 階段

Step7&8: 測量另一個(gè) Text,同上

Step9: Column 拿到兩個(gè)子 Text 返回的 Size 后,計(jì)算出自己的 Size,不難猜到其計(jì)算邏輯應(yīng)該是 width = maxOf(child1.w, child2.w),height = sumOf(child1.h, child2.h)。設(shè)置自己的 width 和 height 后,對(duì)兩個(gè)子 Text 進(jìn)行 Place,垂直線性擺放。

看一下代碼是如何實(shí)現(xiàn)這三步。

所有的 Composable 最終都會(huì)調(diào)用一個(gè)公共 Layout Composable 方法,這里面創(chuàng)建 LayoutNode 存儲(chǔ)在 Composition 節(jié)點(diǎn)樹。

ba6b6c02-da36-11ef-9310-92fbcf53809c.png

以 Column 的實(shí)現(xiàn)為例,可以看到調(diào)用 Layout 時(shí),傳入了三個(gè)參數(shù):

@Composable
inline fun Column(
    modifier: Modifier = Modifier,
    verticalArrangement: Arrangement.Vertical = Arrangement.Top,
    horizontalAlignment: Alignment.Horizontal = Alignment.Start,
    content: @Composable ColumnScope.() -> Unit
) {
    val measurePolicy = columnMeasurePolicy(verticalArrangement, horizontalAlignment)
    Layout(
        content = { ColumnScopeInstance.content() },
        measurePolicy = measurePolicy,
        modifier = modifier
    )
}

content: 在這里定義子 Composable,組合過后形成當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)

measurePolicy: 這是定義了布局的三步走核心邏輯

modifier: 修飾符鏈,參與到布局或者繪制階段

measurePolicy 和 modifier 會(huì)存儲(chǔ)在當(dāng)前 LayoutNode 上,等待 measure 的開始參與其中。下面重點(diǎn)分析 MeasurePolicy 了解三步走如何實(shí)現(xiàn)。

MeasurePolicy - 測量策略

fun interface MeasurePolicy {


    fun MeasureScope.measure(
        measurables: List,
        constraints: Constraints
    ): MeasureResult


}

MeasurePolicy 通過 measure 方法完成測量。這里有兩個(gè)重要參數(shù):

measurables: 等待測量的對(duì)象,其實(shí)就是當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)

constraints: 測量約束。節(jié)點(diǎn)需要基于當(dāng)前的 Constaints 進(jìn)行測量,它規(guī)定了節(jié)點(diǎn)尺寸的上限和下限,如下:

class Constraints {
    val minWidth: Int
    val maxWidth: Int
    val minHeight: Int
    val maxHeight: Int
    ...
}

Constraints - 測量約束

父節(jié)點(diǎn)通過 Constraints 約束子節(jié)點(diǎn)的測量。Constraints 非常重要,我們常說 Compose 不怕布局嵌套正是得益于它。反觀 Android 原生視圖,由于測量階段的約束不明確,子 View 需要再次請求父 View 給出清楚的 View.MeasureSpec,導(dǎo)致出現(xiàn)多次繪制。

舉幾個(gè)例子理解一下 Constraints 如何設(shè)置:

ba8aefbe-da36-11ef-9310-92fbcf53809c.png

對(duì)于頁面的根節(jié)點(diǎn), Activity 的 Window 的長寬就是其 Constraints 的最大長寬。如果是一個(gè)垂直可滾動(dòng)容器的節(jié)點(diǎn),那么它的 Constraints 的 height 應(yīng)該是 Infinity,因?yàn)樗梢钥缍鄠€(gè)屏幕存在。

此外, Modifier 的裝飾能力本質(zhì)也是通過修改 Constraints 完成的。例如 fillMaxWidth 要求被修飾的節(jié)點(diǎn)填充整個(gè)父容器,所以 Modifier 會(huì)在布局階段將 minHeight/minWidth 對(duì)齊 max 組值。關(guān)于 Modifier 參與布局的流程,稍后介紹。

三步走實(shí)現(xiàn) - Kotlin 語法優(yōu)勢的體現(xiàn)

舉例看一下三步走代碼如何實(shí)現(xiàn)。

baa449f0-da36-11ef-9310-92fbcf53809c.png

我們實(shí)現(xiàn)一個(gè)類似 Column 的布局效果,在 measurePolicy#measure 中實(shí)現(xiàn)三步走邏輯。

measurePolicy = { // this: MeasureScope
    // Step1:Measure each children
    val placeables = measurables.map { measurable ->
        measurable.measure(constraints)
    }


    // Step2: Deciee own size
    val height = placeables.sumOf { it.height }
    val width = placeables.maxOf { it.width }


    layout(width, height) { //this: Placeable.PlacementScope


        // Step3: Place children by changing the offset of y co-ord    
        var yPosition = 0


        placeables.forEach { placeable ->
            // Position item on the screen
            placeable.placeRelative(x = 0, y = yPosition)


            // Record the y co-ord placed up to
            yPosition += placeable.height
        }
    }
}

每個(gè) measuable 提供了參與測量的 measure 方法,此處會(huì)傳入 Constraints,返回的 placeable 中已經(jīng)存儲(chǔ)了測量后的 widht 和 height,等待 place

基于各個(gè) placeable 的 w 和 h 計(jì)算當(dāng)前節(jié)點(diǎn)的 Size,并通過 layout 方法設(shè)置。layout 方法內(nèi)會(huì)真正的創(chuàng)建 LayoutNode

layout 方法的末參是一個(gè) lambda,這里是第三步擺放子節(jié)點(diǎn)的邏輯,通過設(shè)置 y 軸的偏移量實(shí)現(xiàn)縱向布局,非常簡單

特別值得一提的是,通過 meause 一個(gè)方法就完成三步走,布局邏輯相對(duì)傳統(tǒng)的 View 系統(tǒng)更加高效,回想傳統(tǒng)自定義 View 你需要分別實(shí)現(xiàn) onMeasure,onLayout,onDraw 等,邏輯分散,可讀性差。

但是這種集中式的寫法有一個(gè)弊端,需要人為保證代碼順序。試想如果把 layout 寫在 measure 前面怎么辦?幸好 Kotlin 強(qiáng)大的編譯期檢查能力,很好地指導(dǎo)大家寫出正確代碼:

measure 方法的返回值是 MeasureResult 類型,layout 方法也返回此類型,所以保證了尾部一定是調(diào)用 layout 完成三步走

Measuable#measure 調(diào)用后返回 Placeable 類型,然后才能調(diào)用 Placeable#place,這保證了 place 和 measure 的先后關(guān)系

Measuable#measure 只能在 MeasureScope 中調(diào)用,Placeable#place 只能在 Placeable.PlacementScope 中調(diào)用,這確保了 place 需要在 layout 的 lambda 中調(diào)用

通過各種返回值類型、作用域類型的約束,大家可以寫出安全又一氣呵成的代碼,這種 API 設(shè)計(jì)理念值得推崇。

Modifier Node

接下來介紹一下 Modifier 如何參與布局的。

bab8a40e-da36-11ef-9310-92fbcf53809c.png

Modifier 在組合之后也會(huì)成為 Node 存儲(chǔ)在節(jié)點(diǎn)樹上,Modifier 的調(diào)用鏈生成一條單向繼承的子節(jié)點(diǎn)樹,而被修飾的 Composable 會(huì)成為這條樹枝的葉子結(jié)點(diǎn)。

比如上面例子中,Image 最終成為 clip->size 的子節(jié)點(diǎn)。實(shí)際上 Image 內(nèi)部有一些內(nèi)置的 Modifier,所以全部展開后 Image 所在的樹枝上有一連串 ModifierNode。

掛在節(jié)點(diǎn)樹上的 ModifierNode 可以參與到深度遍歷的繪制流程中,在 Image 之前對(duì) Constraints 做出調(diào)整,完成對(duì)末端 Image 的裝飾。

以 Padding 修飾符為例,看一下源碼:

//組合中調(diào)用 paddiung 會(huì)
fun Modifier.padding(
    start: Dp = 0.dp,
    top: Dp = 0.dp,
    end: Dp = 0.dp,
    bottom: Dp = 0.dp
) = this then PaddingElement(
    start = start,
    top = top,
    end = end,
    bottom = bottom
)


//Element 存儲(chǔ)到鏈上,創(chuàng)建 PaddingNode
private class PaddingElement(
    ...
) : ModifierNodeElement() 




//PaddingNode 定義 measure 邏輯
private class PaddingNode(


    overide fun MeasureScope.measure(
        measurable: Measurable, // 注意不是list
        constraints: Constraints
    ): MeasureResult {
        ...
    }


):LayoutModifierNode,Modifier.Node()

組合階段,Modifier#then 創(chuàng)建 Element 加入 Modifier chain 中。Element 是無狀態(tài)的,重組中會(huì)重新生成,Element 會(huì)在組合中創(chuàng)建有狀態(tài)的 ModifierNode。ModifierNode 有狀態(tài),重組中僅當(dāng)狀態(tài)發(fā)生變化時(shí)被更新,否則不會(huì)重新生成。Modifier Node 是 Compose 1.5 引入的新優(yōu)化,目的就是通過存儲(chǔ) Modifier 狀態(tài)參與比較,提升重組性能。

ModifierNode 按照參與的階段不同,分為 LayoutModifierNode 和 DrawModifierNode。對(duì)于前者,布局邏輯就是現(xiàn)在 LayoutModifierNode#measure 中,和 MeasurePolicy#measure 的功能一樣,唯一的區(qū)別是接受單個(gè) measurable 參數(shù)而不是 List。因?yàn)槲覀冎懒?ModifierNode 是單向繼承,所以只會(huì)有一個(gè)后續(xù)子節(jié)點(diǎn)。如果把LayoutNode 的 measure 看做是自定義 ViewGroup 需要針對(duì)多個(gè)子 View 布局,那么 LayoutModifierNode 的 measure 更像是自定義 View,只對(duì)自身負(fù)責(zé)。

Modifier.layout {}

除了自定義一個(gè) Modifier 來改變當(dāng)前節(jié)點(diǎn)的布局,還有一個(gè)簡單的方法就是使用 Modifier.layout {} 方法。

fun Modifier.layout(
    measure: MeasureScope.(Measurable, Constraints) -> MeasureResult
)
我們可以在 Modifier 調(diào)用鏈的任意位置插入 measure 自定義代碼,對(duì)當(dāng)前節(jié)點(diǎn)做裝飾。例如下面代碼中添加了一個(gè)自定義 50px 的 padding。
Box(Modifier
    .background(Color.Gray)
    .layout { measurable, constraints ->
        // an example modifier that adds 50 pixels of vertical padding
        val padding = 50
        val placeable = measurable.measure(constraints.offset(vertical = -padding))
        layout(placeable.width, placeable.height + padding) {
            placeable.placeRelative(0, padding)
        }
    }){ ... }

Modifier 布局流程

bade81ec-da36-11ef-9310-92fbcf53809c.png

上面代碼繪制一個(gè)居中擺放 50*50 的矩形。我們通常不會(huì)同時(shí)設(shè)置這么多 size 相關(guān)的 modifier,這個(gè)例子只是為了展示 Modifier 的布局流程:

bafa741a-da36-11ef-9310-92fbcf53809c.png

先看一下自頂向下的測量流程: 從 fillMaxSize 對(duì)應(yīng)的 LayoutModifierNode 出發(fā),假設(shè)當(dāng)前的 Constraints 是 w:0-200,h:0-300。fillMaxSize 的功能是讓子節(jié)點(diǎn)填滿當(dāng)前全部剩余空間,會(huì)為子節(jié)點(diǎn)創(chuàng)建以下 childConstraints:

val childConstraints = Constraints (
    minWidth = outerConstraints.maxWidth,
    maxWidth = outerConstraints.maxWidth,
    minHeight = outerConstraints.maxHeight,
    maxHeight = outerConstraints.maxHeight,
)
來到 warpContentSize,它會(huì)讓子自己決定 size 不設(shè)限,min 值再次回歸 0,childConstraints 如下:
val childConstraints = Constraints (
    minWidth = 0,
    maxWidth = outerConstraints.maxWidth,
    minHeight = 0,
    maxHeight = outerConstraints.maxHeight,
)
來到 size(50),這里自然要給一個(gè)具體的 size 約束,如下:
val childConstraints = Constraints (
    minWidth = 50,
    maxWidth = 50,
    minHeight = 50,
    maxHeight = 50,
)
以此類推 Constraints 經(jīng)過不斷調(diào)整傳入到葉子節(jié)點(diǎn) Box 對(duì)應(yīng)的 LayoutNode,完成三步走。 第一步測量

bb16c8cc-da36-11ef-9310-92fbcf53809c.png

葉子節(jié)點(diǎn)測量完后,再自底向上進(jìn)行第二三步,整個(gè)流程不做贅述了,只提一點(diǎn): wrapContentSize 從語義上是應(yīng)該跟隨子節(jié)點(diǎn)的大小,即 5050,為什么實(shí)際尺寸設(shè)置了 200300 呢?

因?yàn)槠涓腹?jié)點(diǎn) fillMaxSize 傳入的 Constraints 是 200300,rwapContentSize 必須填滿這個(gè)空間,而由于它有一個(gè)默認(rèn)參數(shù) align = Alignment.Center,所以才能出現(xiàn) 5050 矩形塊居中的效果。

Intrinsic Measurements - 固有特性測量

中文將其翻譯成 "固有特性",很多人不理解 "固有" 到底指什么?所以放在本文最后討論一下。

Compose 要求布局過程中每個(gè)節(jié)點(diǎn)只被測量一次,測量總耗時(shí)只與節(jié)點(diǎn)數(shù)正相關(guān),與層級(jí)無關(guān),所以 ComopseUI 不怕嵌套過深,而傳統(tǒng) Android 視圖系統(tǒng)中,某個(gè) View 存在多次測量的情況,隨著層級(jí)變多測量次數(shù)會(huì)指數(shù)級(jí)增長,所以傳圖視圖下我們需要通過優(yōu)化 View 的層級(jí)提升性能。

Compose 為了保證 "每個(gè)節(jié)點(diǎn)只測量一次" 的原則,甚至增加了編譯期檢查:

val constraints1 = ...
val constraints2 = ...
val placeable1 = measurable.measure(constraints1
val placeable2 = measurable.measure(constraints2)

bb280286-da36-11ef-9310-92fbcf53809c.png

"每個(gè)節(jié)點(diǎn)只測量一次"在提升性能的同時(shí)也帶來了問題。來自官方文檔的例子:

@Composable
fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) {
    Row(modifier = modifier) {
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(start = 4.dp)
                .wrapContentWidth(Alignment.Start),
            text = text1
        )
        Divider(
            color = Color.Black,
            modifier = Modifier.fillMaxHeight().width(1.dp)
        )
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(end = 4.dp)
                .wrapContentWidth(Alignment.End),


            text = text2
        )
    }
}
上面代碼的本意是希望打造以下的布局效果:

但發(fā)現(xiàn)實(shí)際效果不符合預(yù)期: Divider 的高度沒有對(duì)齊左右的 Text,而是撐滿了容器高度:

Row 為測量 Divider 傳入Constraints 時(shí),不知道對(duì)齊 Text 高度應(yīng)該設(shè)置怎樣的 maxHeight。傳入的 maxHeight 值比較大導(dǎo)致 Divider 的 fillMaxSize 撐滿了整個(gè)容器。 傳統(tǒng)視圖體系中類似的情況,Row 在測量了 Text 的高度后,會(huì)再測量一次 Divider 并給出更合適的 View.MeasureSpec,但 Compose 中不可以,因?yàn)檫@樣違反了 "每個(gè)節(jié)點(diǎn)只測量一次"的原則。

為此, Compose 引入了 "固有特性測量" 的機(jī)制。在當(dāng)前節(jié)點(diǎn)正式發(fā)起深度遍歷子測量節(jié)點(diǎn)之前的一次 "預(yù)處理",從子節(jié)點(diǎn)提前獲取必要信息,設(shè)置更合理的 Constraints,然后再發(fā)起正式測量。 MeasurePolicy 中提供了獲取 "固有特性" 尺寸的方法: IntrinsicMeasureScope.minIntrinsicXXX

fun interface MeasurePolicy {


   fun IntrinsicMeasureScope.minIntrinsicWidth(
        measurables: List,
        height: Int
    ): Int


   fun IntrinsicMeasureScope.minIntrinsicHeight
   fun IntrinsicMeasureScope.maxIntrinsicWidth
   fun IntrinsicMeasureScope.maxIntrinsicHeight


}
Text 的固有特性的 minIntrinsicHeight 是文本內(nèi)容單行展示的高度;Divider 的 minIntrinsicHeight 是 0,當(dāng)我們改一下例子中的代碼,在 Row 的Modifier.height 增加 IntrinsicSize.Min。
Row(modifier = modifier.height(IntrinsicSize.Min)) {...}
Row 在發(fā)起子節(jié)點(diǎn)測量前,通過 MeasurePolicy 提供的固有特性相關(guān)方法,獲取所有子節(jié)點(diǎn)的minIntrinsicHeight,取最大的一個(gè)設(shè)為 Constraints.maxHeight 后發(fā)起正式測量。這樣,Divider 的 fillMaxSize 就會(huì)跟 Text 兩邊高度對(duì)齊了。

看到這里相信大家理解 "固有"的含義了,其本質(zhì)代表 "不依賴 Constraints"就可以獲取的值,基于這些值更新 Constraints,后續(xù)測量只有一次也能正確約束。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • Android
    +關(guān)注

    關(guān)注

    12

    文章

    3972

    瀏覽量

    130136
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4379

    瀏覽量

    64762
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4899

    瀏覽量

    70625

原文標(biāo)題:【GDE 分享】一文看懂 Jetpack Compose 布局流程

文章出處:【微信號(hào):Google_Developers,微信公眾號(hào):谷歌開發(fā)者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    Jetpack Compose 的基本布局

    mcu
    橙群微電子
    發(fā)布于 :2024年05月21日 15:48:29

    PCB工藝流程詳解

    PCB工藝流程詳解PCB工藝流程詳解
    發(fā)表于 05-22 14:46

    compose的使用技巧是什么?

    compose的使用技巧是什么?
    發(fā)表于 11-15 07:27

    詳解Jetpack Compose 1.1版本的新功能

    我們一如既往地搭建產(chǎn)品路線圖,現(xiàn)在已經(jīng)發(fā)布了 Jetpack Compose 的 1.1 版本,這是 Android 的現(xiàn)代原生界面工具包。此版本新增了一些功能,比如經(jīng)過優(yōu)化的焦點(diǎn)處理、觸摸目標(biāo)值
    的頭像 發(fā)表于 03-11 10:14 ?1655次閱讀

    如何使用 Compose 進(jìn)行構(gòu)建

    適用于 Wear OS 的 Compose 已推出了開發(fā)者預(yù)覽版,使用 Compose 構(gòu)建 Wear OS 應(yīng)用,不僅可以輕松遵循 Material You 指南,同時(shí)可以將 Compose 的優(yōu)點(diǎn)發(fā)揮出來。
    的頭像 發(fā)表于 03-17 13:44 ?2046次閱讀

    Jetpack Compose基礎(chǔ)知識(shí)科普

    Jetpack Compose 是用于構(gòu)建原生 Android 界面的新工具包。它可簡化并加快 Android 上的界面開發(fā),使用更少的代碼、強(qiáng)大的工具和直觀的 Kotlin API,快速讓應(yīng)用生動(dòng)
    的頭像 發(fā)表于 04-02 13:38 ?3347次閱讀

    Android Studio Dolphin穩(wěn)定版正式發(fā)布

    預(yù)覽動(dòng)畫。此外,針對(duì)應(yīng)用界面調(diào)試,我們還在布局檢查器 (Layout Inspector) 中引入了一個(gè)很好用的 Compose 界面計(jì)數(shù)工具,用以跟蹤界面重新組合的次數(shù)。 Jetpack C
    的頭像 發(fā)表于 10-12 19:37 ?2834次閱讀

    Compose Material 3 穩(wěn)定版現(xiàn)已發(fā)布 | 2022 Android 開發(fā)者峰會(huì)

    (新一代 Material Design ) 構(gòu)建 Jetpack Compose 界面。立即開始在應(yīng)用中使用 Material Design 3 吧! Compose Material 3
    的頭像 發(fā)表于 11-21 18:10 ?1560次閱讀

    Jetpack Compose 更新一覽 | 2022 Android 開發(fā)者峰會(huì)

    作者 /?Android 開發(fā)者關(guān)系工程師 Jolanda Verhoef 去年我們發(fā)布了 Jetpack Compose ,此后一直在進(jìn)行優(yōu)化。我們已添加了新的功能并創(chuàng)造出功能更強(qiáng)大的工具,幫助
    的頭像 發(fā)表于 11-23 17:55 ?1486次閱讀

    Google計(jì)劃用Jetpack Compose來重建Android系統(tǒng)中的設(shè)置應(yīng)用

    上周,Google 發(fā)布了 Android 14 的首個(gè)開發(fā)者預(yù)覽版,除了那些最新的功能以外,Google 似乎還正在默默醞釀一個(gè)新的計(jì)劃 —— 用更現(xiàn)代的 Jetpack Compose 來逐步
    的頭像 發(fā)表于 02-18 11:16 ?1882次閱讀

    Compose for Wear OS 1.1 推出穩(wěn)定版: 了解新功能!

    為 Wear OS 構(gòu)建出色的響應(yīng)式應(yīng)用。 ? Compose for Wear OS?1.1 版本 https://developer.android.google.cn/jetpack
    的頭像 發(fā)表于 02-22 01:30 ?1259次閱讀

    Kotlin聲明式UI框架Compose Multiplatform支持iOS

    ,基于 Kotlin 和?Jetpack Compose?打造,由 JetBrains 和開源貢獻(xiàn)者開發(fā)。 Jetpack Compose 是 Google 為構(gòu)建原生 UI 打造的
    的頭像 發(fā)表于 04-24 09:12 ?1665次閱讀
    Kotlin聲明式UI框架<b class='flag-5'>Compose</b> Multiplatform支持iOS

    Jetpack Compose和設(shè)備類型的三大重要更新

    2024 年 Google I/O 大會(huì)上我們分享了大量更新和公告,幫助開發(fā)者提升工作效率。了解 2024 年 Google I/O 大會(huì)上有關(guān) Jetpack Compose 和設(shè)備類型的三大重要更新。
    的頭像 發(fā)表于 08-09 17:07 ?991次閱讀

    docker-compose配置文件內(nèi)容詳解以及常用命令介紹

    一、Docker Compose 簡介 Docker Compose是一種用于定義和運(yùn)行多容器Docker應(yīng)用程序的工具。通過一個(gè)? docker-compose.yml ?文件,您可以配置應(yīng)用程序
    的頭像 發(fā)表于 12-02 09:29 ?4386次閱讀
    docker-<b class='flag-5'>compose</b>配置文件內(nèi)容<b class='flag-5'>詳解</b>以及常用命令介紹

    Docker Compose的常用命令

    。它通過一個(gè)配置文件(docker-compose.yml)來詳細(xì)定義多個(gè)容器之間的關(guān)聯(lián)、網(wǎng)絡(luò)設(shè)置、服務(wù)端口等信息。使用一條簡單的命令,就可以輕松啟動(dòng)、停止和管理這些容器,極大地簡化了多容器應(yīng)用的部署與管理流程,方便實(shí)現(xiàn)應(yīng)用的快速構(gòu)建、開發(fā)、測試以及部署。
    的頭像 發(fā)表于 04-30 13:40 ?430次閱讀

    電子發(fā)燒友

    中國電子工程師最喜歡的網(wǎng)站

    • 2931785位工程師會(huì)員交流學(xué)習(xí)
    • 獲取您個(gè)性化的科技前沿技術(shù)信息
    • 參加活動(dòng)獲取豐厚的禮品