Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Adopting Jetpack Compose in Your Existing Proje...
Search
Somkiat Khitwongwattana
November 17, 2024
Technology
250
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Adopting Jetpack Compose in Your Existing Project - GDG DevFest Bangkok 2024
"Adopting Jetpack Compose in your existing project" at GDG DevFest Bangkok 2024
Somkiat Khitwongwattana
November 17, 2024
More Decks by Somkiat Khitwongwattana
See All by Somkiat Khitwongwattana
Snackbar in Compose with Friendly UI Testing
akexorcist
0
320
Gemini in Android Studio - Google I/O Bangkok '25
akexorcist
0
600
Why App Signing Matters for Your Android Apps - Android Bangkok Conference 2024
akexorcist
1
890
Building the automated Android UI testing in Continuous integration at LINE MAN Wongnai
akexorcist
1
1k
Building the automated Android UI testing in Continuous integration at LINE MAN Wongnai
akexorcist
1
110
What's new in Android 14 - IO Extended George Town 2023
akexorcist
1
410
All you need to know about new Logcat in Android Studio - DevFest Bangkok 2022
akexorcist
0
140
Backend for mobile app - Droidcon SG 2022
akexorcist
0
140
Backend for mobile app - Android Bangkok Conference 2022
akexorcist
1
240
Other Decks in Technology
See All in Technology
⾮構造化データを価値に変える AI エージェント基盤とその組織
ivry_presentationmaterials
0
200
AIにシークレットを見せない、WSL2で安全な開発環境
yazm
0
310
PHPカンファレンス関西の招待状 / Invitation to PHP Conference Kansai
ykanoh
0
210
オントロジーって 結局なにもの? AI エージェントに「意味」を渡す知識設計の話
greenmidori
0
230
触りたくないデータに、どう触るか AI時代のRBACアクセスガバナンス
ivry_presentationmaterials
0
280
コンテナ運用におけるネットワーク・コンテナコストの削減方法/How to Reduce Network and Container Costs in Container Operations
masakiokuda
2
300
しろおびから始める!ログ活用の第一歩
shumei_ito
0
960
FinOps Agentが生んだ技術負債:コスト自動調査の落とし穴と解消までの道のり
oasis1994liveforever
2
140
今さら理解するウェブフォントver.2026
takanorip
3
900
[JAWS-UG彩の国埼玉#12]そろそろ準備する?re:Invent2026をやさしく解説 / reinvent2026-sainokuni
sh_fk2
1
360
スプシの手作業からの解脱 ~更新はClaude Codeに、見せ方と使い方はGASアプリに任せる~
kentarofujii
0
160
NDIでApple Vision Proにリアルタイム映像送信
jyouryuusui
0
130
Featured
See All Featured
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
2
950
The browser strikes back
jonoalderson
0
1.8k
Building a Scalable Design System with Sketch
lauravandoore
464
34k
The #1 spot is gone: here's how to win anyway
tamaranovitovic
4
1.2k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
123
22k
Visualization
eitanlees
153
17k
Claude Code のすすめ
schroneko
67
230k
Bash Introduction
62gerente
615
220k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
33
5.3k
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
630
Designing for humans not robots
tammielis
254
26k
Paper Plane (Part 1)
katiecoart
PRO
2
12k
Transcript
Adopting Jetpack Compose in Your Existing Project Bangkok Somkiat Khitwongwattana
Android GDE
Less code Intuitive Accelerate development Powerful Why Jetpack Compose?
Less code Intuitive Accelerate development Powerful Why Jetpack Compose?
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"> <Button
android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" /> </LinearLayout> Build an UI with XML
class MainActivity : AppCompatActivity() { private val binding: ActivityMainBinding by
lazy { ... } override fun onCreate(savedInstanceState: Bundle?) { ... binding.button.setOnClickListener { // Do something } } } View binding
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {
... setContent { ComposeAppTheme { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center, ) { Button(onClick = { // Do something }) { Text(text = "Button") Build an UI with Jetpack Compose
setContent { ComposeAppTheme { Box( ... ) { Button(onClick =
{ // Do something }) { Text(text = "Button") } } } } Build an UI with Jetpack Compose
Less code Intuitive Accelerate development Powerful Why Jetpack Compose?
<Button android:id="@+id/button3" android:text="Button 3" android:visibility="gone" /> <LinearLayout> <Button android:id="@+id/button1" android:text="Button
1" /> <Button android:id="@+id/button2" android:text="Button 2" /> </LinearLayout> Readability
<Button android:id="@+id/button3" android:text="Button 3" android:visibility="gone" /> <LinearLayout> <Button android:id="@+id/button1" android:text="Button
1" /> <Button android:id="@+id/button2" android:text="Button 2" /> </LinearLayout> Readability
val button1: Button = /* ... */ val button2: Button
= /* ... */ val button3: Button = /* ... */ val isPurchased = false button1.visibility = if (isPurchased) View.GONE else View.VISIBLE button2.visibility = if (isPurchased) View.GONE else View.VISIBLE button3.visibility = if (isPurchased) View.VISIBLE else View.GONE Readability
val button1: Button = /* ... */ val button2: Button
= /* ... */ val button3: Button = /* ... */ val isPurchased = true button1.visibility = if (isPurchased) View.GONE else View.VISIBLE button2.visibility = if (isPurchased) View.GONE else View.VISIBLE button3.visibility = if (isPurchased) View.VISIBLE else View.GONE Readability
val isPurchased: Boolean = ... Box( ... ) { if
(isPurchased) { Button( ... ) { Text("Button 3") } } else { Row( ... ) { Button( ... ) { Text("Button 1") } Spacer( ... ) Button( ... ) { Text("Button 2") } } } } Readability
Less code Intuitive Accelerate development Powerful Why Jetpack Compose?
data class Item( val id: String, val text: String, )
Dynamic list UI
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/textViewItem" android:layout_width="match_parent"
android:layout_height="wrap_content" /> </FrameLayout> Dynamic list UI
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent"
android:layout_height="match_parent" /> </FrameLayout> Dynamic list UI
class ItemViewHolder( private val binding: LayoutListItemBinding ) : ViewHolder(binding.root) {
fun bind(item: Item) { binding.textViewItem.text = item.text } } Dynamic list UI
class ListAdapter(private val items: List<Item>) : RecyclerView.Adapter<ItemViewHolder>() { override fun
onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( LayoutListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) ) override fun getItemCount(): Int = items.size override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { items.getOrNull(position)?.let { holder.bind(it.text) } } } Dynamic list UI
val items: List<Item> = /* ... */ val context: Context
= /* ... */ with(binding.recyclerView) { layoutManager = LinearLayoutManager( context, LinearLayoutManager.VERTICAL, false, ) adapter = ListAdapter(items) } Dynamic list UI
@Composable fun ListContent(items: List<Item>) { LazyColumn(modifier = Modifier.fillMaxSize()) { items(
items = items, key = { item -> item.id } ) { item -> Text( modifier = Modifier.fillMaxWidth(), text = item.text, ) } } Dynamic list UI
Less code Intuitive Accelerate development Powerful Why Jetpack Compose?
var activated by remember { mutableStateOf(true) } val contentColor by
animateColorAsState( ... ) val containerColor by animateColorAsState( ... ) val scale by animateFloatAsState( ... ) Button( modifier = Modifier.scale(scale), colors = ButtonDefaults.buttonColors( containerColor = containerColor, contentColor = contentColor, ), onClick = { activated = !activated }, ) { Text(if (activated) "Activated" else "Inactivated") } Animation
Jetpack Compose 1.7.5 is now available
Declarative programming paradigm Declarative paradigm shift Dynamic content Recomposition Thinking
in Compose
Declarative paradigm shift • Relatively stateless and don't expose setter
or getter functions • Update the UI by passing the different arguments • Unidirectional data flow
State flows down
Event flows up
@Composable fun ProfileContent(name: String, status: String, onEditProfile: () -> Unit)
{ Row { Box{ Image( ... ) IconButton(onClick = onEditProfile) { ... } } Column { Text(name) Text(status) } } } State flows down and event flows up
@Composable fun ProfileContent(name: String, status: String, onEditProfile: () -> Unit)
{ Row { Box{ Image( ... ) IconButton(onClick = onEditProfile) { ... } } Column { Text(name) Text(status) } } } State flows down and event flows up
@Composable fun ProfileContent(name: String, status: String, onEditProfile: () -> Unit)
{ Row { Box{ Image( ... ) IconButton(onClick = onEditProfile) { ... } } Column { Text(name) Text(status) } } } State flows down and event flows up
@Composable fun ProfileContent(name: String, status: String, onEditProfile: () -> Unit)
{ Row { Box{ Image( ... ) IconButton(onClick = onEditProfile) { ... } } Column { Text(name) Text(status) } } } State flows down and event flows up
@Composable fun TopThreeItems(items: List<String>) { Column { HeaderContent() items.take(3).forEach {
item -> ItemContent(item) } } } Dynamic content
@Composable fun TopThreeItems(items: List<String>) { Column { HeaderContent() items.take(3).forEach {
item -> ItemContent(item) } } } Recomposition New value update
@Composable fun TopThreeItems(items: List<String>) { Column { HeaderContent() items.take(3).forEach {
item -> ItemContent(item) } } } Recomposition Skip
@Composable fun TopThreeItems(items: List<String>) { Column { HeaderContent() items.take(3).forEach {
item -> ItemContent(item) } } } Recomposition Recomposition
Get started Jetpack Compose for Android Developers https://developer.android.com/courses/jetpack-compose/course
Get started
Get started https://github.com/android/nowinandroid
Get started https://github.com/chrisbanes/tivi
Why should you adopt Jetpack Compose?
Chance for the big improvement Animate with a few lines
of code Powerful preview in Android Studio Testable without view ID Friendly for sub-component creation Benefit for teams
Powerful preview in Android Studio
@Composable fun MainScreen( ... ) { Column( ... ) {
Profile( modifier = Modifier.testTag("profile"), ... ) ... } } Testable without view ID
<node index="0" text="" resource-id="" class="android.view.View" package="com.akexorcist.devfest" content-desc="" checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,66][79,132]"> <node index="0" text="Android" resource-id="" class="android.widget.TextView" package="com.akexorcist.devfest" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,66][79,132]" /> </node> Testable without view ID
<node index="0" text="" resource-id="" class="android.view.View" package="com.akexorcist.devfest" content-desc="" checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,66][79,132]"> <node index="0" text="Android" resource-id="" class="android.widget.TextView" package="com.akexorcist.devfest" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,66][79,132]" /> </node> Testable without view ID
<node index="0" text="" resource-id="" class="android.view.View" package="com.akexorcist.devfest" content-desc="" checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,66][79,132]"> <node index="0" text="Android" resource-id="" class="android.widget.TextView" package="com.akexorcist.devfest" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,66][79,132]" /> </node> Testable without view ID
Friendly for sub-component creation
Fragment Friendly for sub-component creation Fragment Fragment Fragment ViewModel ViewModel
ViewModel ViewModel ViewModel
View Friendly for sub-component creation View View View ViewModel
Compose Friendly for sub-component creation Compose Compose Compose ViewModel ViewModel
ViewModel ViewModel ViewModel
@Composable fun FirstSection(viewModel: FirstViewModel = koinViewModel()) { ... } @Composable
fun SecondSection(viewModel: SecondViewModel = koinViewModel()) { ... } @Composable fun ThirdSection(viewModel: ThirdViewModel = koinViewModel()) { ... } @Composable fun FourthSection(viewModel: FourthViewModel = koinViewModel()) { ... } Friendly for sub-component creation
@Composable fun MainScreen(viewModel: MainViewModel = koinViewModel()) { Column { FirstSection()
SecondSection() ThirdSection() FourthSection() } } Friendly for sub-component creation
@Composable fun MainScreen(viewModel: MainViewModel = koinViewModel()) { LazyColumn { item(key
= "first") { FirstSection() } item(key = "second") { SecondSection() } item(key = "third") { ThirdSection() } item(key = "fourth") { FourthSection() } } } Friendly for sub-component creation
Activity Result Requesting Runtime Permissions ViewModel Data Streams Asynchronous Operations
Availability
Screen Navigation Dependency Injection Google Maps Navigation Drawer Bottom Sheet
Availability
Nested Scrolling Material 3 Components Dialog Snackbar Image Loading Availability
WindowInsets Handling screen size changed Availability UI Testing
Compose preview generation with Gemini
Things to know Debug build performance Profile installer for better
performance No cost from XML layout inflation Efficiently handle deep UI trees Smart recompositions
Sunflower app https://github.com/android/sunflower
APK Size Based on information from the Sunflower app Views
only 2,252 KB 3,034 KB 2,966 KB Mixed Views and Compose Compose-only
Build Time Based on information from the Sunflower app Views
only 299.47 ms 399.09 ms 342.16 ms Mixed Views and Compose Compose-only
Tivi app https://github.com/chrisbanes/tivi
Based on information from the Tivi app v1.0.0-rc01 APK Size
Views only 4.14 MB 2.71 MB 2.32 MB Fragment and Compose Compose-only
Method Count Based on information from the Tivi app v1.0.0-rc01
Views only 40,029 35,165 23,689 Fragment and Compose Compose-only
In the other side Different knowledge Few performance overhead based
on complexity Fewer third-party libraries compared to the View Limited legacy support
None
Approach to integrating Jetpack Compose New Features Simple Component or
Screen Redesign Features
Fragment Layout Layout View Layout View View Compose
Activity Activity Activity Activity Activity View View View Compose
Activity Fragment Fragment Fragment Fragment View View View Compose
Activity Activity Activity Fragment Navigation View Compose Fragment View Compose
Compose
Activity Activity Activity Fragment Navigation View Compose Fragment View Compose
Compose
Activity Fragment Fragment Fragment Navigation View Compose Fragment View Compose
Compose
Activity Fragment Fragment Fragment Navigation View Compose Fragment View Compose
Compose
Activity Fragment Fragment Fragment Navigation View Compose Fragment View Compose
Compose
Codebase Navigation Existing UI Component & Design System Analytics Testing
Things to think about
Compose in Activity Fragment View Compose View in
class MainActivity: ComponentActivity() { override fun onCreate( ... ) {
... setContent { // In Compose world } } } Compose in Activity
class HomeFragment : Fragment() { override fun onCreateView( ... ):
View { return ComposeView(requireContext()).apply { setViewCompositionStrategy( ... ) setContent { // In Compose world } } } } Compose in Fragment
Compose in View <?xml version="1.0" encoding="utf-8"?> <FrameLayout> ... <androidx.compose.ui.platform.ComposeView android:id="@+id/composeView"
android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout>
val composeView = findViewById<ComposeView>(R.id.composeView) composeView.apply { setViewCompositionStrategy( ... ) setContent
{ // In Compose world } } Compose in View
@Composable fun AndroidCustomView(title: String, onClick: () -> Unit) { AndroidView(
factory = { context -> CustomView(context).apply { setOnClickListener(onClick) } }, update = { view -> view.setTitle(title) } ) View in Compose
Codebase preparation for seamless feature development Start with small, incremental
changes Learning resources for future adopters Negotiate with the product's decision-maker To make it happen
API Maturity Learning resources Libraries & tools Backward compatibility Multiplatform
supported Jetpack Compose is now production-ready Performance Wide Adoption
"Any code you built with views will become technical debt"
Florina Muntenescu
Thank you Bangkok