Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[단축본] 컴포즈 내부로 이해하는 최적화 비법

[단축본] 컴포즈 내부로 이해하는 최적화 비법

Ji Sungbin

April 29, 2023
Tweet

More Decks by Ji Sungbin

Other Decks in Programming

Transcript

  1. ஹನૉղࠗ۽੉೧ೞח୭੸ച࠺ߨ
    ױ୷ࠄ
    ✨૑ࢿ࠼

    View Slide

  2. 👋૑ࢿ࠼
    🏠TVOHCJO
    🏰TVOHCJOMBOE
    🐙HJUIVCDPNKJTVOHCJO

    View Slide

  3. /PEF4ZTUFN
    $PNQPTF6*
    4OBQTIPU4ZTUFN
    8SBQVQ
    📖ݾର

    View Slide

  4. /PEF4ZTUFN
    $PNQPTF6*
    4OBQTIPU4ZTUFN
    8SBQVQ
    📖ݾର
    $PNQPTF3VOUJNF
    $PNQPTF6*


    +FUQBDL$PNQPTF

    View Slide

  5. ↟ӝળ
    ↟੗ࣁೠѐ֛ࢤۚ
    📝द੘ೞӝ੹ী

    View Slide

  6. 1. Node System
    *OUSPEVDJOH/PEF
    4MPU5BCMF(SPVQ
    !3FBE0OMZ$PNQPTBCMF
    $PNQPTJUJPO 3VOUJNF
    3FDPNQPTJUJPO4NBSU
    %POVUIPMFTLJQQJOH

    View Slide

  7. @Composable


    fun UiNodes() {


    Column {


    repeat(3) { index ->


    Text(text = "My index is $index")


    }


    }


    }
    Column
    Text
    Text
    Text

    View Slide

  8. @Composable


    fun UiNodes() {


    Column {


    repeat(3) { index ->


    Text(text = "My index is $index")


    }


    }


    }
    Ui Node
    Ui Node
    Ui Node
    Ui Node

    View Slide

  9. $PNQPTF3VOUJNF/PEFJOUFSGBDF
    $PNQPTF6*/PEFJNQMFNFOUBUJPO

    View Slide

  10. /**


    * ஹನૉ੄ ݽٚ ؘ੉ఠܳ ੷੢ೞҊ ҙܻೞח ௿ېझੑפ׮.


    * Gap Buffer ੗ܐҳઑ৬ ےؒ ঘࣁझ۽ ҳഅعҊ, ղࠗ ؘ੉ఠח Arrayী ੷੢ؾפ׮.


    */


    internal class SlotTable : CompositionData, Iterable


    View Slide

  11. Gap Buffer: ࢎਊೡ ҕрٜਸ ޷ܻ ഛࠁ೧ فҊ, ೧׼ ҕрীࢲ ੘সਸ ૓೯ೞח ੗ܐ ҳઑ.


    ਤఃೖ٣ইীࢲח زੌೠ ਤ஖ Ӕ୊ী ௿۞झఠ݂ػ ബਯ੸ੋ ࢗੑ ߂ ࢏ઁ ੘সਸ ೲਊೞח ز੸ ߓৌ


    ੉ۄҊ ੿੄ೞҊ ੓णפ׮.

    View Slide

  12. Gap Buffer: زੌೠ ਤ஖ Ӕ୊ী ௿۞झఠ݂ػ ബਯ੸ੋ ࢗੑ ߂ ࢏ઁ ੘সਸ ೲਊೞח ز੸ ߓৌ


    1, ୡӝ Gap ࢤࢿ: O(N)


    [(v)_, _, _, _, _, _, _, _, _, _] // _ח Gap, (v)ח അ੤ cursorܳ աఋն

    View Slide

  13. Gap Buffer: زੌೠ ਤ஖ Ӕ୊ী ௿۞झఠ݂ػ ബਯ੸ੋ ࢗੑ ߂ ࢏ઁ ੘সਸ ೲਊೞח ز੸ ߓৌ


    1, ୡӝ Gap ࢤࢿ: O(N)


    [(v)_, _, _, _, _, _, _, _, _, _] // _ח Gap, (v)ח അ੤ cursorܳ աఋն


    2. 0ߣ૩ ੋؙझী Hi ࢗੑ


    [(v)H, i, _, _, _, _, _, _, _, _] // ݽف Gapী ࢗੑغ޲۽ ୶о Gap ࢤࢿ੉ ೙ਃೞ૑ ঋই O(1)ী ৮ܐؽ

    View Slide

  14. Gap Buffer: زੌೠ ਤ஖ Ӕ୊ী ௿۞झఠ݂ػ ബਯ੸ੋ ࢗੑ ߂ ࢏ઁ ੘সਸ ೲਊೞח ز੸ ߓৌ


    1, ୡӝ Gap ࢤࢿ: O(N)


    [(v)_, _, _, _, _, _, _, _, _, _] // _ח Gap, (v)ח അ੤ cursorܳ աఋն


    2. 0ߣ૩ ੋؙझী Hi ࢗੑ


    [(v)H, i, _, _, _, _, _, _, _, _] // ݽف Gapী ࢗੑغ޲۽ ୶о Gap ࢤࢿ੉ ೙ਃೞ૑ ঋই O(1)ী ৮ܐؽ


    3. 1ߣ૩ ੋؙझ੄ i ઁѢ


    [H, (v)_, _, _, _, _, _, _, _, _]


    // cursorܳ iо ੓ח ਤ஖۽ ৤ӝӝ ਤ೧ O(N)੉ ѦܻҊ


    // ੉റ Gapীࢲ ч ઁѢо ૓೯غ޲۽ O(1)݅ী ৮ܐؽ (ч ઁѢח ೧׼ чਸ Gapਵ۽ ߸҃ೞח Ѫਵ۽ ҳഅؾפ׮)

    View Slide

  15. Gap Buffer: زੌೠ ਤ஖ Ӕ୊ী ௿۞झఠ݂ػ ബਯ੸ੋ ࢗੑ ߂ ࢏ઁ ੘সਸ ೲਊೞח ز੸ ߓৌ


    1, ୡӝ Gap ࢤࢿ: O(N)


    [(v)_, _, _, _, _, _, _, _, _, _] // _ח Gap, (v)ח അ੤ cursorܳ աఋն


    2. 0ߣ૩ ੋؙझী Hi ࢗੑ


    [(v)H, i, _, _, _, _, _, _, _, _] // ݽف Gapী ࢗੑغ޲۽ ୶о Gap ࢤࢿ੉ ೙ਃೞ૑ ঋই O(1)ী ৮ܐؽ


    3. 1ߣ૩ ੋؙझ੄ i ઁѢ


    [H, (v)_, _, _, _, _, _, _, _, _]


    // cursorܳ iо ੓ח ਤ஖۽ ৤ӝӝ ਤ೧ O(N)੉ ѦܻҊ


    // ੉റ Gapীࢲ ч ઁѢо ૓೯غ޲۽ O(1)݅ী ৮ܐؽ (ч ઁѢח ೧׼ чਸ Gapਵ۽ ߸҃ೞח Ѫਵ۽ ҳഅؾפ׮)


    4. 0ߣ૩ ੋؙझ੄ H ܳ Bye ۽ ߸҃


    [(v)B, y, e, _, _, _, _, _, _, _]


    // cursorܳ Hо ੓ח ਤ஖۽ ৤ӝӝ ਤ೧ O(N)੉ ѦܻҊ


    // ੉റ ݽف Gapীࢲ ч সؘ੉౟о ૓೯غ޲۽ O(1)݅ী ৮ܐؽ

    View Slide

  16. var isLoading = false


    @Composable


    fun Main() {


    Column { // 1ߣ૩ ਤ஖ (അ੤ cursor)


    Text(text = "Column Data")


    if (isLoading) {


    Text(text = "Loading...")


    }


    }


    }


    Column
    Text
    “Column Data”
    Gap
    Gap
    Gap
    Gap
    Gap
    Gap
    ߣ૩ਤ஖

    View Slide

  17. var isLoading = true


    @Composable


    fun Main() {


    Column { // 1ߣ૩ ਤ஖ (੉੹ cursor)


    Text(text = "Column Data")


    if (isLoading) { // 2ߣ૩ ਤ஖ (അ੤ cursor)


    Text(text = "Loading...")


    }


    }


    }


    Column
    Text
    “Column Data”
    Gap
    Gap
    Gap
    Gap
    Text
    “Loading…”
    ߣ૩ਤ஖
    ߣ૩ਤ஖

    View Slide

  18. /**


    * Gapਸ ޖदೞҊ ౠ੿ ਤ஖੄ ੺؀੸ੋ ੋؙझܳ оܰఃח ېಌੑפ׮. (ےؒ ঘࣁझ ഝࢿച)


    *


    * ےؒ ঘࣁझ: ؘ੉ఠܳ ੷੢ೞח ࠶۾ਸ ೠߣী ৈ۞ ѐ ঘࣁझೞח Ѫ੉ ইפۄ


    * ౠ੿ ਤ஖۽ ߄۽ ੽Ӕೞৈ ೠ ߣী ೞա੄ ࠶۾ਸ ঘࣁझೞח ߑध


    */


    internal class Anchor(loc: Int) {


    internal var location = loc


    val valid get() = location != Int.MIN_VALUE


    fun toIndexFor(slots: SlotTable) = slots.anchorIndex(this)


    fun toIndexFor(writer: SlotWriter) = writer.anchorIndex(this)


    }

    View Slide

  19. var isLoading = true


    @Composable


    fun Main() {


    Column {


    Text(text = "Column Data")


    if (isLoading) { // Anchor۽ ߄۽ ੽Ӕ


    Text(text = "Loading...")


    }


    }


    }


    Column
    “Column Data”
    Gap
    Gap
    Gap
    Gap
    Text
    “Loading…”
    "ODIPS۽߄۽੽Ӕ
    Text

    View Slide

  20. @Composable


    fun Main() {


    Column {


    Text(text = "Column Data")


    }


    }


    Column
    Text
    “Column Data”
    Gap
    Gap
    Gap
    Gap
    Gap
    Gap

    View Slide

  21. @Composable


    fun Main() {


    Column {


    Text(text = "Column Data")


    }


    }


    “Column Data”
    Gap
    Gap
    Gap
    Gap
    Gap
    Gap
    Group
    Group

    View Slide

  22. “Column Data”
    Gap
    Gap
    Gap
    Gap
    Gap
    Gap
    Group
    Group
    3FTUBSUBCMF(SPVQ
    3FQMBDFBCMF(SPVQ
    .PWBCMF(SPVQ
    @Composable


    fun Main() {


    Column {


    Text(text = "Column Data")


    }


    }


    View Slide

  23. // RestartableGroup: ܻஹನ૑࣌੉ ੌযզ ࣻ ੓ח ஹನ੷࠶


    // ઱ਤ۽ ࢤࢿػ׮. ߹ب੄ ২࣌੉ হ׮ݶ ݽٚ ஹನ੷࠶ী


    // ࢤࢿغח Ӓܛ੉Ҋ, ܻஹನ૑࣌ ೞח ߑߨਸ оܰ஘׮.


    // ݽٚ RestartableGroup਷ ೧׼ ߧਤ ݅ఀ


    // ੗୓੄ ܻஹನ૑࣌ झ௏೐(RecomposeScope)ܳ ഋࢿೠ׮.


    @Composable


    fun RestartableContainer() {


    Text(text = "SungbinLand")


    }


    RestartableGroup
    “SungbinLand”
    Gap
    Gap
    Gap
    Gap
    Gap
    Gap
    RestartableGroup
    3FTUBSUBCMF$POUBJOFS
    3FDPNQPTF4DPQF 5FYU
    3FDPNQPTF4DPQF

    View Slide

  24. // ReplaceableGroup: ࠙ӝী ٮۄ ੤ߓ஖ ؼ ࣻ ੓ח


    // ஹನ੷࠶ ઱ਤ۽ ࢤࢿػ׮.


    // Ӓܛਸ Ү୓೧ঠ ೡ ٸ ੘ࢿػ ؘ੉ఠܳ ੿ܻೞח ߑߨਸ оܰ஘׮.


    @Composable


    fun ReplaceableContainer(isColumn: Boolean = true) {


    when (isColumn) {


    true -> Text(text = "Column")


    else -> Text(text = "Row")


    }


    }


    RestartableGroup
    “Column”
    Gap
    Gap
    Gap
    Gap
    Gap
    ReplaceableGroup
    3FQMBDFBCMF$POUBJOFS XIFO
    RestartableGroup
    5FYU

    View Slide

  25. /**


    * ஹನ੷࠶੉ ߓ஖ػ ਤ஖о ׳ۄ૑ݶ ਤ஖ ݫݽ੉ઁ੉࣌੄ keyо ׳ۄઉࢲ ؘ੉ఠо ୡӝചؾפ׮.


    * ؘ੉ఠܳ ਬ૑ೠ ࢚క۽ ਤ஖ܳ ৤ӝӝ ਤ೧ࢶ key ஹನ੷࠶ਸ ੉ਊ೧


    * ਤ஖ ݫݽ੉ઁ੉࣌ীࢲ ଵҊೡ keyܳ ૒੽ ੿੄೧ঠ ೤פ׮.


    *


    * key ஹನ੷࠶ਸ ੉ਊ೧ ߓ஖ػ ஹನ੷࠶਷ ઱ਤ۽ MovableGroup੉ ࢤࢿؾפ׮.


    * MovableGroup਷ ஹನ੷࠶੉ ਤ஖ ݫݽ੉ઁ੉࣌ key৬ ؘ੉ఠܳ ೦࢚ ࠁઓೞݴ


    * ੉زೞח ߑߨਸ оܰ஝פ׮.


    */


    @Composable


    inline fun key(


    vararg keys: Any?,


    block: @Composable () -> T


    )


    @Composable


    fun MovableContainer() {


    key(Any()) {


    Text(text = "Key")


    }


    }


    RestartableGroup
    “Key”
    Gap
    Gap
    Gap
    Gap
    Gap
    MovableGroup
    .PWBCMF$POUBJOFS LFZ
    RestartableGroup
    5FYU

    View Slide

  26. !3FBE0OMZ$PNQPTBCMF
    object MaterialTheme {


    val colors: Colors


    @Composable


    @ReadOnlyComposable


    get() = LocalColors.current


    val typography: Typography


    @Composable


    @ReadOnlyComposable


    get() = LocalTypography.current


    // …


    }


    @Composable


    @ReadOnlyComposable


    fun stringResource(@StringRes id: Int): String {


    val resources = resources()


    return resources.getString(id)


    }

    View Slide

  27. var isLoading = false


    @Composable


    fun Main() {


    Column {


    Text(text = "Column Data")


    if (isLoading) {


    Text(text = "Loading...")


    }


    }


    }


    Column
    Text
    “Column Data”
    Gap
    Gap
    Gap
    Gap
    Gap
    Gap
    Composition

    View Slide

  28. var isLoading = true


    @Composable


    fun Main() {


    Column {


    Text(text = "Column Data")


    if (isLoading) {


    Text(text = "Loading...")


    }


    }


    }


    Column
    Text
    “Column Data”
    Gap
    Gap
    Gap
    Gap
    Text
    “Loading…”
    Recomposition

    View Slide

  29. @Composable


    fun Main() {


    var number by remember { mutableStateOf(0) }


    Button(onClick = { number++ }) {


    Text(text = number.toString())


    }


    }


    Button
    Function0
    Gap
    Gap
    Gap
    Gap
    Text
    “$number”
    Smart-Recomposition
    Gap

    View Slide

  30. EPOVUIPMFTLJQQJOH
    setContent {


    var number by remember { mutableStateOf(0) }


    println("setContent recomposition")


    Text(


    modifier = Modifier.clickable { number++ },


    text = number.toString()


    ).also { println("Text recomposition") }


    }


    /*


    ܻஹನ૑࣌੉ য٣ী ૓೯ؼөਃ?


    1, setContent content ৔৉


    2. Text


    3. setContent content ৔৉ + Text


    */

    View Slide

  31. EPOVUIPMFTLJQQJOH
    setContent {


    var number by remember { mutableStateOf(0) }


    println("setContent recomposition")


    Text(


    modifier = Modifier.clickable { number++ },


    text = number.toString()


    ).also { println("Text recomposition") }


    }


    /*


    [first-composition] [re-composition]


    setContent recomposition setContent recomposition


    Text recomposition Text recomposition


    ੉റ زੌ ۽Ӓ ߈ࠂ


    */

    View Slide

  32. EPOVUIPMFTLJQQJOH
    setContent {


    var number by remember { mutableStateOf(0) }


    println("setContent recomposition")


    Button(onClick = { number++ }) {


    Text(


    text = number.toString()


    ).also { println("Text recomposition") }


    }.also { println("Button recomposition") }


    }


    /*


    [first-composition] [re-composition]


    setContent recomposition Text recomposition


    Text recomposition ੉റ زੌ ۽Ӓ ߈ࠂ


    Button recomposition


    */

    View Slide

  33. EPOVUIPMFTLJQQJOH
    // ஹ౵ੌ ੹


    @Composable


    fun TextWrapper(text: String) {


    Text(text = text)


    }


    // ஹ౵ੌ റ (೨ब ࠗ࠙݅ ಴द)


    fun TextWrapper(composer: Composer, text: String) {


    // ղࠗীࢲ addRecomposeScope() ۽ ܻஹನ૑࣌ झ௏೐ܳ ୶оೞҊ ੓਺


    composer.startRestartGroup(-199242123)


    Text(text = text)


    composer.endRestartGroup()


    }

    View Slide

  34. EPOVUIPMFTLJQQJOH
    setContent {


    var number by remember { mutableStateOf(0) }


    println("setContent recomposition")


    Text(


    modifier = Modifier.clickable { number++ },


    text = number.toString()


    ).also { println("Text recomposition") }


    }


    setContent {


    var number by remember { mutableStateOf(0) }


    println("setContent recomposition")


    Button(onClick = { number++ }) {


    Text(


    text = number.toString()


    ).also { println("Text recomposition") }


    }.also { println("Button recomposition") }


    }


    Text RecomposeScope

    Text RecomposeScope

    View Slide

  35. EPOVUIPMFTLJQQJOH
    setContent {


    var number by remember { mutableStateOf(0) }


    println("setContent recomposition")


    Button(onClick = { number++ }) {


    Text(


    text = number.toString()


    ).also { println("Text recomposition") }


    }.also { println("Button recomposition") }


    }


    Text RecomposeScope

    Button RecomposeScope

    View Slide

  36. 2. Compose UI
    %SBXJOH1SPDFTT.PEJGJFSPQUJNJ[F
    $PNQPTJUJPO 6*

    View Slide

  37. Composition Layout Draw
    @Composable


    fun Main() {


    Text(


    modifier = Modifier


    .clickable { getOne() },


    text = "Hello, World!"


    )


    }


    fun getOne() = 1

    View Slide

  38. @Composable


    fun Main() {


    Text(


    modifier = Modifier


    .clickable { getOne() },


    text = "Hello, World!"


    )


    }


    fun getOne() = 1
    RestartableGroup
    Function0
    Gap
    Gap
    Composition Layout Draw
    “Hello, World!”
    Gap
    Gap
    Gap
    Gap

    View Slide

  39. Modifier.offset { IntOffset(x = 0, y = 0) }


    Modifier.layout { measurable, constraints ->


    val placeable = measurable.measure(constraints)


    layout(width = 0, height = 0) {


    placeable.place(x = 0, y = 0, zIndex = 1f)


    }


    }


    Modifier.graphicsLayer {


    scaleX = 1f; rotationX = 0f; clip = false


    shape = RectangleShape; translationX = 0f


    alpha = 1f; shadowElevation = 0f


    }
    SomeGroup
    Function0
    Gap
    Composition Layout Draw
    Function0
    Gap
    Gap
    Gap
    Gap
    Function0

    View Slide

  40. Modifier.offset { IntOffset(x = 0, y = 0) }


    Modifier.layout { measurable, constraints ->


    val placeable = measurable.measure(constraints)


    layout(width = 0, height = 0) {


    placeable.place(x = 0, y = 0, zIndex = 1f)


    }


    }


    Modifier.graphicsLayer {


    scaleX = 1f; rotationX = 0f; clip = false


    shape = RectangleShape; translationX = 0f


    alpha = 1f; shadowElevation = 0f


    }
    SomeGroup
    Function0
    Gap
    Composition Layout Draw
    Function0
    Gap
    Gap
    Gap
    Gap
    Function0
    Skippable

    View Slide

  41. RestartableGroup
    Function0
    Gap
    Gap
    Composition Layout Draw
    “Hello, World!”
    Text
    Int
    “Hello,
    World!”
    ۨ੉ইਓઁডઑѤ DPOTUSBJOUT
    ҅࢑
    ठܶప੉࠶ч҅࢑ JOWPLF

    6*౟ܻҳ୷

    View Slide

  42. Composition Layout Draw
    Text
    Int
    “Hello,
    World!”

    View Slide

  43. @Composable


    fun Main() {


    Text(


    modifier = Modifier


    .clickable { getOne() },


    text = "Hello, World!”


    )


    }


    fun getOne() = 1
    RestartableGroup
    Function0
    Gap
    Gap
    Composition Layout Draw
    “Hello, World!”
    Gap
    Gap
    Gap
    Gap

    View Slide

  44. Composition Layout
    (SPVQѐ

    View Slide

  45. Composition Layout
    (SPVQѐ
    ҅࢑೧ঠೞח(SPVQѐ
    $PNQPTJUJPO௼ӝীٮۄ0 /
    ૐо

    View Slide

  46. Composition Layout Draw
    SubComposition
    ࠗݽ੄ઁডઑѤ֢୹
    ן਷ୡӝച

    View Slide

  47. Composition Layout Draw
    Composition
    DPOTUSBJOUT
    ࠗݽ੄ઁডઑѤ֢୹
    @Stable


    interface BoxWithConstraintsScope : BoxScope {


    val constraints: Constraints


    val minWidth: Dp


    val maxWidth: Dp


    val minHeight: Dp


    val maxHeight: Dp


    }

    View Slide

  48. Composition Layout Draw
    ן਷ୡӝച
    @Composable


    fun LazyLayout()


    @Composable


    fun LazyRow()


    @Composable


    fun LazyColumn()
    -B[Z-BZPVU਷ചݶীաఋաӝ߄۽੹ী-BZPVU%SBX૓೯

    View Slide

  49. Composition SubComposition

    View Slide

  50. Composition
    SubComposition SubComposition
    SubComposition

    SubComposition
    😅

    View Slide

  51. 3. Snapshot System
    *OUSPEVDJOH4OBQTIPU
    5JNF$PNQMFYJUZ
    $PNQPTJUJPO-PDBM

    View Slide

  52. State CompositionLocal
    SnapshotStateList

    View Slide

  53. 4OBQTIPU

    View Slide

  54. 4OBQTIPU
    ஹನ੷࠶਷प೯غחࣽࢲо੿೧ઉ੓૑ঋ׮
    ೞա੄4UBUFоزदী߸҃ؼࣻ੓਺
    ࢚క୽ج

    View Slide

  55. 4OBQTIPU
    ݒ࢚క߸҃ਸ೧׼DBMMTJUFীҊ݀غѱ૓೯ೞҊ
    ୶റҊ݀ػ߸҃ਸਗࠄ࢚కী߈৔ೞחӝࣿ
    ࢚క߸҃੄Ҋ݀ࢿदझమ

    View Slide

  56. 4OBQTIPU

    View Slide

  57. 4OBQTIPU
    🤔

    View Slide

  58. 4OBQTIPU
    SomeGroup
    1 Snapshot
    Gap
    Gap
    Gap
    Gap
    Gap
    Gap
    Gap

    View Slide

  59. 4OBQTIPU

    SomeGroup
    1 Snapshot
    Gap
    Gap
    Gap
    Gap
    Gap
    Gap
    4OBQTIPU
    2 Snapshot

    View Slide

  60. 4OBQTIPU

    SomeGroup
    1 Snapshot
    Gap
    Gap
    Gap
    Gap
    Gap
    4OBQTIPU
    2 Snapshot
    4OBQTIPU
    3 Snapshot

    View Slide

  61. /4OBQTIPU
    SomeGroup
    1 Snapshot
    4 Snapshot
    6 Snapshot
    2 Snapshot
    3 Snapshot
    5 Snapshot
    N Snapshot
    7 Snapshot

    View Slide

  62. SomeGroup
    1 Snapshot
    4 Snapshot
    6 Snapshot
    2 Snapshot
    3 Snapshot
    5 Snapshot
    N Snapshot
    7 Snapshot
    4OBQTIPU3FBE
    4OBQTIPU8SJUF

    View Slide

  63. SomeGroup
    px - snapshot
    px4 - snapshot
    px2 - snapshot
    px3 - snapshot
    @Composable


    fun Main() {


    val px = 1.dp.px


    val px2 = 2.dp.px


    val px3 = 3.dp.px


    val px4 = 4.dp.px


    }


    inline val Dp.px


    @Composable


    @ReadOnlyComposable


    get() = with(LocalDensity.current) {


    toPx()


    }


    Gap
    Gap
    Gap
    Gap

    View Slide

  64. SomeGroup
    density - snapshot
    @Composable


    fun Main() {


    val density = LocalDensity.current


    val px = with(density) {


    1.dp.toPx()


    }


    val px2 = with(density) {


    2.dp.toPx()


    }


    val px3 = with(density) {


    3.dp.toPx()


    }


    val px4 = with(density) {


    4.dp.toPx()


    }


    }
    Gap
    Gap
    Gap
    Gap
    Gap
    Gap
    Gap

    View Slide

  65. DPNQPTJUJPO-PDBM0G
    val LocalTextSelectionColors = compositionLocalOf {


    DefaultTextSelectionColors


    }


    val LocalTextStyle = compositionLocalOf {


    TextStyle.Default


    }


    val LocalConfiguration = compositionLocalOf(


    neverEqualPolicy()

    ) {


    noLocalProvidedFor("LocalConfiguration")


    }


    val LocalContentAlpha = compositionLocalOf {


    1f


    }
    SomeGroup
    CompositionLocal
    CompositionLocal
    Composable
    CompositionLocal
    CompositionLocal
    Composable
    Composable
    Composable

    View Slide

  66. TUBUJD$PNQPTJUJPO-PDBM0G
    val LocalContext = staticCompositionLocalOf {


    noLocalProvidedFor("LocalContext")


    }


    val LocalClipboardManager = staticCompositionLocalOf {


    noLocalProvidedFor("LocalClipboardManager")


    }


    val LocalDensity = staticCompositionLocalOf {


    noLocalProvidedFor("LocalDensity")


    }


    val LocalFocusManager = staticCompositionLocalOf {


    noLocalProvidedFor("LocalFocusManager")


    }
    StaticCompositionLocal
    Composable
    Composable
    Composable
    Composable
    Composable
    SomeGroup
    Composable
    Composable

    View Slide

  67. ↟ೣԋࠁݶજਸ੗ܐ
    4. Wrap-up
    -BNCEB0QUJNJ[JOH
    -JWF-JUFSBM
    $PNQJMFS.FUSJDT
    4OBQTIPU4ZTUFNXJUI.7$$
    #BTFMJOF1SPGJMFT
    $PNQPTBCMF(SPVQT
    4UBCJMJUZ4ZTUFN

    View Slide

  68. хࢎ೤פ׮
    ஹನૉ ղࠗ ௏٘ח cs.android.comীࢲ оઉ৳णפ׮.


    ੉ ೐ۨઃప੉࣌ীח షझ౱ীࢲ ઁҕೠ షझಕ੉झо ੸ਊغয ੓णפ׮.


    QnA🙋

    View Slide