Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
I/O extended 2021 double S Compose 시작하기
Search
Veronikapj
June 30, 2021
Programming
300
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
I/O extended 2021 double S Compose 시작하기
6/30 Compose 시작하기
Veronikapj
June 30, 2021
More Decks by Veronikapj
See All by Veronikapj
Google I/O 요약만 봤다고? AI가 못 보는 진짜 이야기
veronikapj
0
88
The Know-how of Seniors in the Age of AI
veronikapj
0
140
Gemini in Android Studio I/O extended 2024
veronikapj
0
460
How to use Macrobenchmark
veronikapj
0
390
앱 성능 영혼까지 끌어올리기
veronikapj
0
1.2k
Software Productivity - Devfest Songdo 2023
veronikapj
0
1.1k
파이어베이스로 서비스 품질 올리기
veronikapj
0
410
What's new in Android IO23 Daejeon
veronikapj
1
350
KC23_Coroutine_Testing
veronikapj
0
190
Other Decks in Programming
See All in Programming
20260623_Loop Engineeringで自分の分身の問い合わせBotを作る
ryugen04
0
200
コーディングルールの鮮度を保ちたい for SRE NEXT 2026 / keep-fresh-go-internal-conventions-sre-next-2026
handlename
0
140
Hatena Engineer Seminar #37「言語モデルの活用に関する研究」
slashnephy
0
510
【やさしく解説 設計編・中級 #1】一つの車に、運転手は一人 ~ある倉庫システムの事例から~
panda728
PRO
0
160
Go1.27で導入されるジェネリクスメソッドでできること
mackee
0
280
壊れたパーサから始める関数型設計と構成的なパーサ #fp_matsuri
raiga0310
2
190
「なぜそう決めたのか」を残し続ける仕組み ― Notion AI カスタムエージェント × Slack連携による設計判断の自動記録 - NIKKEI Tech Talk #47
niftycorp
PRO
0
260
【やさしく解説 設計編・中級 #6】良いアーキテクチャとは ~ 一本の登り道の、行き先 ~
panda728
PRO
0
130
共通化で考えるべきは、実装より公開する型だった
codeegg
0
210
Vue × Nuxt × Oxc どこまで使える?実運用の現在地
andpad
0
370
The Bowling Game- From Imperative to Functional Programming - Part 1
philipschwarz
PRO
0
310
symfony/aiとlaravel/boost
77web
0
120
Featured
See All Featured
Bootstrapping a Software Product
garrettdimon
PRO
307
120k
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
410
Site-Speed That Sticks
csswizardry
13
1.2k
Ten Tips & Tricks for a 🌱 transition
stuffmc
0
150
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
420
Everyday Curiosity
cassininazir
0
250
Tell your own story through comics
letsgokoyo
1
990
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
680
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.4k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
The Pragmatic Product Professional
lauravandoore
37
7.4k
Transcript
ߓ - Android Engineer, Coupan g veronikapj Compose दೞӝ Android
1
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 ) } } } }
3 ٘ хࣗ ࣻ ٘۽ ؊ ݆ সਸ ೞҊ
ߡӒ ېझܳ ߑೡ ࣻ ਵ۽ ٘о рױೞݴ ਬ ҙܻೞӝ औणפ. ҙ UI݅ ࢸݺೞݶ աݠח Composeীࢲ ܻפ. জ ࢚కо ߸҃غݶ UIо زਵ ۽ সؘؾפ. ࡅܲ ѐߊ җ ӝઓ ݽٚ ٘৬ ഐജغ۽ ઁ য٣ࢲ ٚ ਗೞח ۽ ࢎਊೡ ࣻ णפ. पदр ܻࠁӝ ߂ ৮ೠ Android झౚ٣য় ਗ ਵ۽ ࡅܰѱ ߈ࠂೡ ࣻ णפ. ъ۱ೠ ࢿמ Android ۖಬ APIী ঘࣁझೞҊ ݠ ౭ܻ ٣ੋ, যف ప݃, গפݫ࣌ ١ ਸ ӝࠄਵ۽ ਗೞח ݧ জਸ ٜ݅ ࣻ णפ. https://developer.android.com/jetpack/compose
4 layout? XML?
5 <TextVie w android:id="@+id/textview_first " android:layout_width=“. . . ” android:layout_height=“.
. . ” android:text="Hello, Android " / > @Composabl e fun Greeting() { Text(text = “Hello, Android!") }
6 <TextVie w android:id="@+id/textview_first " android:layout_width=“. . . ” android:layout_height=“.
. . ” android:text="Hello, Android " / > @Composabl e fun Greeting() { Text(text = “Hello, Android!") } ৵ ߄Բѱ غਸө? Declarative UI Patterns ( i/o 19 ) https://youtu.be/VsStyq4Lzxo
7 Android Studio New Project > Minimum SDK UI toolkit
ী ࢜۽ ӝמਸ ઁҕ ೡ ٸ ݃ ӝࠄ API ߡਸ ৢ۰ঠ ೞӝ ٸޙী ৮ ઁҕ غ۰ݶ য় ے दр ਃפ.
8 UI toolkitী ೠ ࣻࢎ೦ب ߄۽ ਊ https://developer.android.com/jetpack “Jetpack” Composeੋ
ਬ?
9 ӝઓ View ٣ੋী ೠ ѐࢶ ӝઓ API
10 public class Button extends TextView { } ӝઓ API
11 public class Button extends TextView { } ߡౡীࢲ ೞҊ
र সࠁ ؊ ݆ Ѧ ೡ ࣻ . ӝઓ API
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
13 MyFragment.kt my_fragment.xml attr.xml style.xml value.xml زੌೠ ҳࢿ ਃࣗܳ ਤೠ
ցޖ ݆ ٘ ࠙
14 TextView Button LinearLayout xml layout ইఃఫী ೠ Ҋ
15 TextView Button LinearLayout findViewById() tv.text b.visibility vg.addView() xml layout
Acitvity / Fragment Ѿبо ֫ ইఃఫী ೠ Ҋ
16 Compose Function Paradigm ࢶഋ ಁ۞ ജ
17 Declarative (ࢶഋ) vs Imperative (ݺ۸ഋ) о 99ѐ ࢚ݶ ࠛ
ࠁѱ ೞҊ ژ о 0ѐ ࢚ݶ ઙо ࠁѱ ೞҊ о 0ѐ ࢚ݶ Ӓ ं ߓо ࠁѱ ೞ ݫੌ জী ੍ ঋ ݫद ইਸ दೞח UI Understanding Compose (summit 19) https://youtu.be/Q9MtlmmN4Q0
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 (ݺ۸ഋ)
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 (ݺ۸ഋ)
20 @Composable fun BadgeEnvelope(count: Int) { Envelope(fire=count>99, paper=count > 0)
{ if (count > 0) { Badge(text="$count") } } } Declarative (ࢶഋ)
21 @Composable fun BadgeEnvelope(count: Int) { Envelope(fire=count>99, paper=count > 0)
{ if (count > 0) { Badge(text="$count") } } } View.Visibility?
22 @Composable fun Greetings(name: String) { Text (text = "Hello
$name!") } ݻ о ౠ ܲ Composable ೣࣻ ߧਤ ղীࢲ݅ ഐ @Composable য֢ప࣌ ୶о return? ইޖ Ѫب ߈ജೞ ঋ زੌೠ ੋࣻ۽ ৈ۞ ߣ ഐ ؼ ٸ زੌೠ ߑधਵ۽ ز ߸ࣻ ژח random() ഐ ࢎਊ X Composable ೣࣻח
23
24 Compose Project दೞӝ Compose UIܳ द ܻ ࠅ ࣻ
ח ӝמ ߂ ࢜ ۽ં మ݁җ э झ݃ ಞӝ ӝמਸ ࢎਊ
25 EmptyComposeActivity Compose Project दೞӝ ӝࠄ Compose ѐߊജ҃ ҳࢿػ ۽ંܳ
दೡ ࣻ
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 दೞӝ
27 @Composable fun ArtistCard() { Text("Alfred Sisley") Text("3 minutes ago")
} Compose Layout
28 Vertica l LinearLayout Horizonta l LinearLayout FrameLayout ӝࠄ ۨইਓ
29 @Composable fun ArtistCard(artist: Artist) { Row(verticalAlignment = Alignment.CenterVertically) {
Image(/*...*/) Column { Text(artist.name) Text(artist.lastSeenOnline) } } }
XML Attribute 30 @Composable fun ArtistCard(/*...*/) { val padding =
16.dp Column( Modifier .clickable() .padding(padding) .fillMaxWidth() ) { // rest of the implementation } } Composable ӝ, ۨইਓ, ز ߂ ݽন ߸҃ Ӕࢿ ۄ߰җ э ࠁ ୶о ࢎਊ ੑ۱ ܻ ਃࣗܳ ܼ оמ, झ܀ оמ, ٘ېӒ оמ ژח ഛ/୷ࣗ оמೞѱ ݅٘ח Ѫҗ э ֫ ࣻળ ࢚ഐਊ ୶о Modifier
31 Row( modifier .padding(16.dp) .background(backgroundColor) ) { // content }
32 Row( modifier .background(backgroundColor) .padding(16.dp) ) { // content }
Row(
33 Row(
34 Button( text = "Button", icon: Icon? = myIcon, textStyle
= TextStyle(...), spacingBetweenIconAndText = 4.dp, ... ) Slot API
35 Button( Row { MyImage() Spacer(4.dp) Text("Button") } } Slot
API
36 Button { Row { MyImage() Spacer(4.dp) Text("Button") } }
@Composable fun Button( modifier: Modifier = Modifier.None, onClick: (() -> Unit)? = null, ... content: @Composable () -> Unit } Slot API
37 TopAppBar
38 TopAppBar( title = { Text(text = "Page title", maxLines
= 2) }, navigationIcon = { Icon(myNavIcon) } ) TopAppBar
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
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
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
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
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
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
45 Principles of layout in Compose No multi-pass measurement,
ಁझ ஏਸ ೲਊೞ ঋणפ. ف ߣ ࢚ ೞਤ ೦ݾਸ ஏೡ ࣻ হणפ. ف ߣ ஏೞѱ غݶ ۠ఋ ৻о ߊࢤפ.
46 val radius = animate(if (selected) 28.dp else 0.dp) Surface(
shape = RoundedCornerShape) topStart = radius ) ) { ... } Animation
47 Theme
48 <!--Base theme, all common styling here--> <style name="Base.Owl" parent="@style/Theme.MaterialComponents.DayNight.NoActionBar">
<!--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> </style> <style name="Owl" parent="@style/Base.Owl"/> <style name="Owl.Yellow"> <item name="colorPrimary">@color/owl_yellow_500</item> ... </style> theme.xml MaterialTheme( colors = ... typography = ... shapes = ... ) { //content } Theme.kt Theme
49 @Composable fun YellowTheme( content: @Composable () -> Unit )
{ MaterialTheme( colors = YellowLightColors, typography = OwlTypography, shapes = OwlShapes, content = content ) } Theme.kt Theme
50 @Composable fun Onboarding(...) { YellowTheme { Scaffold(...) { ...
} } } MainScreen.kt Theme
51 @Composable fun Onboarding(...) { YellowTheme { Scaffold(...) { BlueTheme
{ Button(...) } } } } MainScreen.kt Theme
52 Migration
53
54 <TextView android:id="@+id/plant_detail_name" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="@dimen/margin_small" android:layout_marginEnd="@dimen/margin_small" android:gravity="center_horizontal" android:text="@{viewModel.plant.name}" android:textAppearance="?attr/textAppearanceHeadline5"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:text="Apple" /> XML Compose
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
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
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
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
59 fragment_example.xml <CoordinatorLayout ...> <AppBarLayout ...> <ConstraintLayout ...> <TextView ...>
<TextView ...> </ConstraintLayout> </CoordinatorLayout> xml Compose Compose in Views
60 fragment_example.xml <ComposeView android:id="@+id/compose_view" android:layout_width="match_parent" android:layout_height="match_parent"/> <CoordinatorLayout ...> <AppBarLayout ...>
<ConstraintLayout ...> <TextView ...> <TextView ...> </ConstraintLayout> </CoordinatorLayout> ConstraintLayoutਸ ComposeView۽ xml Compose Compose in Views
61 class ExampleFragment : Fragment() { override fun onCreateView(...): View?
{ return inflater.inflate( R.layout.fragment_example, container, false ).apply { ... findViewById<ComposeView>(R.id.compose_view).setContent { MaterialTheme { Surface { Text("Hello Compose") //PlantName("ComposeTree!") } } } } } }
62 class ExampleFragment : Fragment() { override fun onCreateView(...): View?
{ return inflater.inflate( R.layout.fragment_example, container, false ).apply { ... findViewById<ComposeView>(R.id.compose_view).setContent { MaterialTheme { Surface { Text("Hello Compose") //PlantName("ComposeTree!") } } } } } } Compose world!
63 class ExampleFragment : Fragment() { override fun onCreateView(...): View?
= ComposeView(requireContext()).apply { setContent { MaterialTheme { Surface { Text("Hello Compose") } } } } }
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 }) } }
65 Android View in Compose @Composable fun <T : View>
AndroidView( viewBlock: (Context) -> T, modifier: Modifier = Modifier, update: (T) -> Unit = NoOpUpdate ) { ... } @Composable private fun MapViewContainer(...) { val mapView = rememberMapView() AndroidView({ mapView }) { map -> map.getMapAsync { ... } }
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
67 github.com/android/compose-samples Custom design System Custom layouts Animation Material Theme
Light/Dark Theme Custom Layout Animation Samples
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
69 github.com/android/compose-samples
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