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
330
Gemini in Android Studio - Google I/O Bangkok '25
akexorcist
0
620
Why App Signing Matters for Your Android Apps - Android Bangkok Conference 2024
akexorcist
1
920
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
430
All you need to know about new Logcat in Android Studio - DevFest Bangkok 2022
akexorcist
0
150
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
An Introduction to Context Ontology Accelerator (teaching AI agents what your data means)
inariku
PRO
1
190
分析作業をどこまで任せるか自分で決められる AIネイティブBI『Golden Analytics』が 良さげな話 /golden-analytics-is-ai-native-bi
shinyaa31
0
130
PostgreSQL オプティマイザ統計の秘密
nori_shinoda
1
440
AWS基盤改善を支える 私たちの理念と行動指針
kentosuzuki
0
140
The Author Pays
hiccupps
0
140
AWS構成から考えるWebセキュリティ ― WordPressの構築経験から考える公開範囲と更新運用【JAWS FESTA 2026 in AKITA】
cscengineer
PRO
2
380
お客様の「やりたい」の先を見通す合意形成プロセス ~裏にある課題と不安を解きほぐすコツ~
yukakoseki
0
130
ライフワーク「猫でもわかるDirectConnect」
kentapapa
0
300
eBPF 簡介 2026 版
shunghsiyu
0
190
会社説明資料_最新版
demaecan
0
32k
Software Engineering in an AI-Driven World
erikjhordan_rey
0
130
公共システムAdventCalendar2026
toru_kubota
0
290
Featured
See All Featured
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
3.3k
A Soul's Torment
seathinner
8
3.9k
Speed Design
sergeychernyshev
33
2.2k
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
1.1k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
Embracing the Ebb and Flow
colly
88
5.2k
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
610
New Earth Scene 8
popppiees
4
2.8k
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
630
AI: The stuff that nobody shows you
jnunemaker
PRO
10
1.3k
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
680
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
63
48k
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