Slide 1

Slide 1 text

ߓ೙઱ - Android Engineer, Coupan g veronikapj Compose द੘ೞӝ Android 1

Slide 2

Slide 2 text

2 @Composable fun JetpackCompose() { Card { var expanded by remember { mutableStateOf(false) } Column(Modifier.clickable { expanded = !expanded}) { Image(painterResource(R.drawable.jetpack_compose)) AnimatedVisibility(expanded) { Text( text = "Jetpack Compose" style = MaterialTheme.typography.h2 ) } } } }

Slide 3

Slide 3 text

3 ௏٘ хࣗ ੸਷ ࣻ੄ ௏٘۽ ؊ ݆਷ ੘সਸ ೞҊ ੹୓ ߡӒ ௿ېझܳ ߑ૑ೡ ࣻ ੓ਵ޲۽ ௏٘о рױೞݴ ਬ૑ ҙܻೞӝ औणפ׮. ૒ҙ੸ UI݅ ࢸݺೞݶ աݠ૑ח Composeীࢲ ୊ ܻ೤פ׮. জ ࢚కо ߸҃غݶ UIо ੗زਵ ۽ সؘ੉౟ؾפ׮. ࡅܲ ѐߊ җ੿ ӝઓ੄ ݽٚ ௏٘৬ ഐജغ޲۽ ঱ઁ য٣ࢲ ٚ ਗೞח ؀۽ ࢎਊೡ ࣻ ੓णפ׮. पदр ޷ܻࠁӝ ߂ ৮੹ೠ Android झౚ٣য় ૑ਗ ਵ۽ ࡅܰѱ ߈ࠂೡ ࣻ ੓णפ׮. ъ۱ೠ ࢿמ Android ೒ۖಬ APIী ૒੽ ঘࣁझೞҊ ݠ ౭ܻ঴ ٣੗ੋ, যف਍ ప݃, গפݫ੉࣌ ١ ਸ ӝࠄ੸ਵ۽ ૑ਗೞח ݧ૓ জਸ ٜ݅ ࣻ ੓णפ׮. https://developer.android.com/jetpack/compose

Slide 4

Slide 4 text

4 layout? XML?

Slide 5

Slide 5 text

5 @Composabl e fun Greeting() { Text(text = “Hello, Android!") }

Slide 6

Slide 6 text

6 @Composabl e fun Greeting() { Text(text = “Hello, Android!") } ৵ ߄Բѱ غ঻ਸө? Declarative UI Patterns ( i/o 19 ) https://youtu.be/VsStyq4Lzxo

Slide 7

Slide 7 text

7 Android Studio New Project > Minimum SDK UI toolkit ী ࢜۽਍ ӝמਸ ઁҕ ೡ ٸ ݃׮ ӝࠄ API ߡ੹ਸ ৢ۰ঠ ೞӝ ٸޙী ৮੹൤ ઁҕ غ۰ݶ য় ے दр੉ ೙ਃ೤פ׮.

Slide 8

Slide 8 text

8 UI toolkitী ؀ೠ ࣻ੿ࢎ೦ب ߄۽ ੸ਊ https://developer.android.com/jetpack “Jetpack” Composeੋ ੉ਬ?

Slide 9

Slide 9 text

9 ӝઓ View ٣੗ੋী ؀ೠ ѐࢶ ӝઓ API

Slide 10

Slide 10 text

10 public class Button extends TextView { } ӝઓ API

Slide 11

Slide 11 text

11 public class Button extends TextView { } ߡౡীࢲ ೞҊ र਷ ੘সࠁ׮ ؊ ݆਷ Ѧ ೡ ࣻ ੓׮. ӝઓ API

Slide 12

Slide 12 text

12 Custom View ܳ ੘ࢿೞӝ ਤ೧ Ҋ޹ೡ Ѫٜ public class MyCustomView extends View { } 1. xml layout ࢤࢿ - ViewGroup ژח View ܳ ࢎਊ 2. attr.xml ా೧ Custom attribute ࢸ੿ 3. TypedArrayܳ CustomView class ীࢲ ௏٘ܳ ా೧ ੸ਊ 4. ೙ਃೠ ҃਋ View lifecycleী ٮۄ ੌࠗ ݫࣗ٘ܳ ੤੿੄ onDraw(), onMeasure() https://developer.android.com/guide/topics/ui/custom-components ӝઓ API

Slide 13

Slide 13 text

13 MyFragment.kt my_fragment.xml attr.xml style.xml value.xml زੌೠ ҳࢿ ਃࣗܳ ਤೠ ցޖ ݆਷ ௏٘੄ ࠙࢑

Slide 14

Slide 14 text

14 TextView Button LinearLayout xml layout ইఃఫ୊ী ؀ೠ Ҋ޹

Slide 15

Slide 15 text

15 TextView Button LinearLayout findViewById() tv.text b.visibility vg.addView() xml layout Acitvity / Fragment Ѿ೤بо ֫׮ ইఃఫ୊ী ؀ೠ Ҋ޹

Slide 16

Slide 16 text

16 Compose Function Paradigm ࢶ঱ഋ ಁ۞׮੐੄ ੹ജ

Slide 17

Slide 17 text

17 Declarative (ࢶ঱ഋ) vs Imperative (ݺ۸ഋ) ஠਍౟о 99ѐ ੉࢚੉ݶ ࠛ੉ ࠁ੉ѱ ೞҊ ژ ஠਍౟о 0ѐ ੉࢚੉ݶ ઙ੉о ࠁ੉ѱ ೞҊ ஠਍౟о 0ѐ ੉࢚੉ݶ Ӓ ஠਍౟ ं੗੄ ߓ૑о ࠁ੉ѱ ೞ੗ ݫੌ জী ੍૑ ঋ਷ ݫद૑ ই੉௑ਸ ಴दೞח UI Understanding Compose (summit 19) https://youtu.be/Q9MtlmmN4Q0

Slide 18

Slide 18 text

18 fun updateCount(count: Int) { if (count > 0 && !hasBadge()) { addBadge() } else if (count == 0 && hasBadge()) { removeBadge() } if (count > 99 && !hasFire()) { addFire() setBadgeText("99+") } else if (count <= 99 && hasFire()) { removeFire() } if (count > 0 && !hasPaper()) { removePaper() } else if (count ==0 && hasPaper()) { addPaper() } if (count <= 99) { setBadgeText("$count") } } Imperative (ݺ۸ഋ)

Slide 19

Slide 19 text

19 fun updateCount(count: Int) { if (count > 0 && !hasBadge()) { addBadge() } else if (count == 0 && hasBadge()) { removeBadge() } if (count > 99 && !hasFire()) { addFire() setBadgeText("99+") } else if (count <= 99 && hasFire()) { removeFire() } if (count > 0 && !hasPaper()) { removePaper() } else if (count ==0 && hasPaper()) { addPaper() } if (count <= 99) { setBadgeText("$count") } } ஠਍౟о 99ѐ ੉࢚੉ݶ ࠛ੉ ࠁ੉ѱ ೞҊ ژ ஠਍౟о 0ѐ ੉࢚੉ݶ ઙ੉о ࠁ੉ѱ ೞҊ ஠਍౟о 0ѐ ੉࢚੉ݶ Ӓ ஠਍౟ ं੗੄ ߓ૑о ࠁ੉ѱ ೞ੗ Imperative (ݺ۸ഋ)

Slide 20

Slide 20 text

20 @Composable fun BadgeEnvelope(count: Int) { Envelope(fire=count>99, paper=count > 0) { if (count > 0) { Badge(text="$count") } } } Declarative (ࢶ঱ഋ)

Slide 21

Slide 21 text

21 @Composable fun BadgeEnvelope(count: Int) { Envelope(fire=count>99, paper=count > 0) { if (count > 0) { Badge(text="$count") } } } View.Visibility?

Slide 22

Slide 22 text

22 @Composable fun Greetings(name: String) { Text (text = "Hello $name!") } ݻ о૑ ౠ૚ ׮ܲ Composable ೣࣻ ߧਤ ղীࢲ݅ ഐ୹ @Composable য֢ప੉࣌ ୶о return? ইޖ Ѫب ߈ജೞ૑ ঋ਺ زੌೠ ੋࣻ۽ ৈ۞ ߣ ഐ୹ ؼ ٸ زੌೠ ߑधਵ۽ ੘ز ੹৉ ߸ࣻ ژח random() ഐ୹ ࢎਊ X Composable ೣࣻח

Slide 23

Slide 23 text

23

Slide 24

Slide 24 text

24 Compose Project द੘ೞӝ Compose UIܳ ૊द ޷ܻ ࠅ ࣻ ੓ח ӝמ ߂ ࢜ ೐۽ં౟ మ೒݁җ э਷ झ݃౟ ಞ૘ӝ ӝמਸ ࢎਊ

Slide 25

Slide 25 text

25 EmptyComposeActivity Compose Project द੘ೞӝ ӝࠄ Compose ѐߊജ҃੉ ҳࢿػ ೐۽ં౟ܳ द੘ೡ ࣻ ੓׮

Slide 26

Slide 26 text

26 plugins { id 'org.jetbrains.kotlin.android' version '1.4.32' } android { buildFeatures { compose true } } dependencies { implementation 'androidx.compose.ui:ui:1.0.0-beta07' // Tooling support (Previews, etc.) implementation 'androidx.compose.ui:ui-tooling:1.0.0-beta07' // Foundation (Border, Background, Box, Image, shapes, animations, etc.) implementation 'androidx.compose.foundation:foundation:1.0.0-beta07' ... } build.gradle https://developer.android.com/jetpack/compose/setup Compose Project द੘ೞӝ

Slide 27

Slide 27 text

27 @Composable fun ArtistCard() { Text("Alfred Sisley") Text("3 minutes ago") } Compose੄ Layout

Slide 28

Slide 28 text

28 Vertica l LinearLayout Horizonta l LinearLayout FrameLayout ӝࠄ ۨ੉ইਓ

Slide 29

Slide 29 text

29 @Composable fun ArtistCard(artist: Artist) { Row(verticalAlignment = Alignment.CenterVertically) { Image(/*...*/) Column { Text(artist.name) Text(artist.lastSeenOnline) } } }

Slide 30

Slide 30 text

XML Attribute 30 @Composable fun ArtistCard(/*...*/) { val padding = 16.dp Column( Modifier .clickable() .padding(padding) .fillMaxWidth() ) { // rest of the implementation } } Composable ੄ ௼ӝ, ۨ੉ইਓ, ز੘ ߂ ݽন ߸҃ ੽Ӕࢿ ۄ߰җ э਷ ੿ࠁ ୶о ࢎਊ੗ ੑ۱ ୊ܻ ਃࣗܳ ௿ܼ оמ, झ௼܀ оמ, ٘ېӒ оמ ژח ഛ؀/୷ࣗ оמೞѱ ݅٘ח Ѫҗ э਷ ֫਷ ࣻળ੄ ࢚ഐ੘ਊ ୶о Modifier

Slide 31

Slide 31 text

31 Row( modifier .padding(16.dp) .background(backgroundColor) ) { // content }

Slide 32

Slide 32 text

32 Row( modifier .background(backgroundColor) .padding(16.dp) ) { // content } Row(

Slide 33

Slide 33 text

33 Row(

Slide 34

Slide 34 text

34 Button( text = "Button", icon: Icon? = myIcon, textStyle = TextStyle(...), spacingBetweenIconAndText = 4.dp, ... ) Slot API

Slide 35

Slide 35 text

35 Button( Row { MyImage() Spacer(4.dp) Text("Button") } } Slot API

Slide 36

Slide 36 text

36 Button { Row { MyImage() Spacer(4.dp) Text("Button") } } @Composable fun Button( modifier: Modifier = Modifier.None, onClick: (() -> Unit)? = null, ... content: @Composable () -> Unit } Slot API

Slide 37

Slide 37 text

37 TopAppBar

Slide 38

Slide 38 text

38 TopAppBar( title = { Text(text = "Page title", maxLines = 2) }, navigationIcon = { Icon(myNavIcon) } ) TopAppBar

Slide 39

Slide 39 text

39 @Composable fun Scaffold( // .. topBar: @Composable (() -> Unit)? = null, bottomBar: @Composable (() -> Unit)? = null, // .. bodyContent: @Composable (PaddingValues) -> Unit ) { } https://developer.android.com/reference/kotlin/androidx/compose/material/package-summary#Scaffold Scaffold

Slide 40

Slide 40 text

40 @Composable fun ConstraintLayoutContent() { ConstraintLayout { val (button, text) = createRefs() Button( modifier = Modifier.constrainAs(button) { top.linkTo(parent.top, margin = 16.dp) } ) { Text("Button") } Text("Text", Modifier.constrainAs(text) { top.linkTo(button.bottom, margin = 16.dp) }) } } ConstraintLayout

Slide 41

Slide 41 text

41 @Composable fun ConstraintLayoutContent() { ConstraintLayout { val (button, text) = createRefs() Button( modifier = Modifier.constrainAs(button) { top.linkTo(parent.top, margin = 16.dp) } ) { Text("Button") } Text("Text", Modifier.constrainAs(text) { top.linkTo(button.bottom, margin = 16.dp) }) } } ConstraintLayout

Slide 42

Slide 42 text

42 @Composable fun ConstraintLayoutContent() { ConstraintLayout { val (button, text) = createRefs() Button( modifier = Modifier.constrainAs(button) { top.linkTo(parent.top, margin = 16.dp) } ) { Text("Button") } Text("Text", Modifier.constrainAs(text) { top.linkTo(button.bottom, margin = 16.dp) }) } } ConstraintLayout

Slide 43

Slide 43 text

43 Custom View ܳ ੘ࢿೞӝ ਤ೧ Ҋ޹ೡ Ѫٜ public class MyCustomView extends View { } 1. xml layout ࢤࢿ - ViewGroup ژח View ܳ ࢎਊ 2. attr.xml ా೧ Custom attribute ࢸ੿ 3. TypedArrayܳ CustomView class ীࢲ ௏٘ܳ ా೧ ੸ਊ 4. ೙ਃೠ ҃਋ View lifecycleী ٮۄ ੌࠗ ݫࣗ٘ܳ ੤੿੄ onDraw(), onMeasure() https://developer.android.com/guide/topics/ui/custom-components

Slide 44

Slide 44 text

44 @Composable fun Layout( childrun: @Composable () -> Unit, // child ߓ஖ modifier: Modifier = Modifier, // ੸ਊೡ Modifier measureBlock: MeasureBlock // ࢎਊ੗ custom behavior ҳഅ ) { } 1. measure each item : п ೦ݾਸ ஏ੿ 2. call layout() : ۨ੉ইਓ ݫࣗ٘ ഐ୹ 3. place each item : п ೦ݾਸ ߓ஖ Custom Layout

Slide 45

Slide 45 text

45 Principles of layout in Compose No multi-pass measurement, ׮઺ ಁझ ஏ੿ਸ ೲਊೞ૑ ঋणפ׮. ف ߣ ੉࢚ ೞਤ ೦ݾਸ ஏ੿ೡ ࣻ হणפ׮. ف ߣ ஏ੿ೞѱ غݶ ۠ఋ੐ ৘৻о ߊࢤ೤פ׮.

Slide 46

Slide 46 text

46 val radius = animate(if (selected) 28.dp else 0.dp) Surface( shape = RoundedCornerShape) topStart = radius ) ) { ... } Animation

Slide 47

Slide 47 text

47 Theme

Slide 48

Slide 48 text

48 <!--Material shape attributes--> <item name=“shapeAppearanceSmallComponent”>@style/…</item> <!--Material type attributes--> <item name="textAppearanceHeadline1">@style/TextAppearance.Owl.Headline1</item> <item name="textAppearanceBody1">@style/TextAppearance.Owl.Body1</item> <item name="colorPrimary">@color/owl_yellow_500</item> ... theme.xml MaterialTheme( colors = ... typography = ... shapes = ... ) { //content } Theme.kt Theme

Slide 49

Slide 49 text

49 @Composable fun YellowTheme( content: @Composable () -> Unit ) { MaterialTheme( colors = YellowLightColors, typography = OwlTypography, shapes = OwlShapes, content = content ) } Theme.kt Theme

Slide 50

Slide 50 text

50 @Composable fun Onboarding(...) { YellowTheme { Scaffold(...) { ... } } } MainScreen.kt Theme

Slide 51

Slide 51 text

51 @Composable fun Onboarding(...) { YellowTheme { Scaffold(...) { BlueTheme { Button(...) } } } } MainScreen.kt Theme

Slide 52

Slide 52 text

52 Migration

Slide 53

Slide 53 text

53

Slide 54

Slide 54 text

54 XML Compose

Slide 55

Slide 55 text

55 @Composable private fun PlantName(name: String) { Text( text = name, style = MaterialTheme.typography.h5, modifier = Modifier .fillMaxWidth() .padding(horizontal = dimensionResource(R.dimen.margin_small)) .wrapContentWidth(Alignment.CenterHorizontally) ) } XML Compose

Slide 56

Slide 56 text

56 @Composable private fun PlantName(name: String) { Text( text = name, style = MaterialTheme.typography.h5, modifier = Modifier .fillMaxWidth() .padding(horizontal = dimensionResource(R.dimen.margin_small)) .wrapContentWidth(Alignment.CenterHorizontally) ) } android:textAppearance="?attr/textAppearanceHeadline5" xml Compose XML Compose

Slide 57

Slide 57 text

57 @Composable private fun PlantName(name: String) { Text( text = name, style = MaterialTheme.typography.h5, modifier = Modifier .fillMaxWidth() .padding(horizontal = dimensionResource(R.dimen.margin_small)) .wrapContentWidth(Alignment.CenterHorizontally) ) } android:layout_width=“match_parent” xml Compose XML Compose

Slide 58

Slide 58 text

58 @Composable private fun PlantName(name: String) { Text( . . . modifier = Modifier .fillMaxWidth() .padding(horizontal = dimensionResource(R.dimen.margin_small)) .wrapContentWidth(Alignment.CenterHorizontally) ) } android:layout_marginStart="@dimen/margin_small" android:layout_marginEnd="@dimen/margin_small" android:gravity="center_horizontal" xml Compose XML Compose

Slide 59

Slide 59 text

59 fragment_example.xml xml Compose Compose in Views

Slide 60

Slide 60 text

60 fragment_example.xml ConstraintLayoutਸ ComposeView۽ ؀୓ xml Compose Compose in Views

Slide 61

Slide 61 text

61 class ExampleFragment : Fragment() { override fun onCreateView(...): View? { return inflater.inflate( R.layout.fragment_example, container, false ).apply { ... findViewById(R.id.compose_view).setContent { MaterialTheme { Surface { Text("Hello Compose") //PlantName("ComposeTree!") } } } } } }

Slide 62

Slide 62 text

62 class ExampleFragment : Fragment() { override fun onCreateView(...): View? { return inflater.inflate( R.layout.fragment_example, container, false ).apply { ... findViewById(R.id.compose_view).setContent { MaterialTheme { Surface { Text("Hello Compose") //PlantName("ComposeTree!") } } } } } } Compose world!

Slide 63

Slide 63 text

63 class ExampleFragment : Fragment() { override fun onCreateView(...): View? = ComposeView(requireContext()).apply { setContent { MaterialTheme { Surface { Text("Hello Compose") } } } } }

Slide 64

Slide 64 text

64 class ExampleFragment : Fragment() { override fun onCreateView(...): View? = LinearLayout(...).apply { addView(ComposeView(...).apply { id = R.id.compose_view_x ... }) addView(TextView(...)) addView(ComposeView(...).apply { id = R.id.compose_view_y }) } }

Slide 65

Slide 65 text

65 Android View in Compose @Composable fun AndroidView( viewBlock: (Context) -> T, modifier: Modifier = Modifier, update: (T) -> Unit = NoOpUpdate ) { ... } @Composable private fun MapViewContainer(...) { val mapView = rememberMapView() AndroidView({ mapView }) { map -> map.getMapAsync { ... } }

Slide 66

Slide 66 text

66 github.com/android/compose-samples Varied UI Light & dark themes Resource loading UI testing Design Theme Resource Loading Back button Handling Architecture Components Animation UI Testing Samples

Slide 67

Slide 67 text

67 github.com/android/compose-samples Custom design System Custom layouts Animation Material Theme Light/Dark Theme Custom Layout Animation Samples

Slide 68

Slide 68 text

68 github.com/android/compose-samples Dynamic Theming Courtines Local storage with Room Draggable UI elements Android Views Inside Compose UI state handling UI tests Samples

Slide 69

Slide 69 text

69 github.com/android/compose-samples

Slide 70

Slide 70 text

Thank you! developer.android.com/ jetpack/compos e Declarative UI Patterns(io19 ) Understanding Compose(summit 19 ) Jetpack Compose ࢎਊೞӝ (android 11 ) What’s new in Jetpack Compose(io 21 ) github.com/android/ compose-samples Resources 70 ߓ೙઱ - Android Engineer, Coupan g veronikapj