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

Jetpack Compose - The Future of Android Development (Extended)

Jetpack Compose - The Future of Android Development (Extended)

In this talk, we take a glimpse at what Jetpack Compose is in a nutshell, how we can start building an app with it, and how we can introduce it to an existing project. The talk includes 2 demo projects.
Presented at a Webuni Android webinar on 2022.04.20., online, in Hungarian.
https://youtu.be/gQBOYHHHBhA?t=114

Az előadásban megnézzük, hogy mi is az a Jetpack Compose dióhéjban, és hogy hogy működik, valamint hogy hogy kezdhetünk el dolgozni vele új és meglévő projectekben, 2 példaproject bemutatásával.
Az előadás a 2022.04.20-i Webuni Android webináriumon hangzott el magyarul.
https://youtu.be/gQBOYHHHBhA?t=114

E8168a08862631072b2e82e7b662dc07?s=128

István Juhos

April 20, 2022
Tweet

More Decks by István Juhos

Other Decks in Programming

Transcript

  1. Webuni webinar – @stewemetal stewemetal The Future of Android Development

    István Juhos Jetpack Compose
  2. Webuni webinar – @stewemetal How we write UI now? •

    View objects inflated from XML layout files • For more than 10 years now 🗓
  3. Webuni webinar – @stewemetal How we write UI now? •

    View objects inflated from XML layout files • For more than 10 years now 🗓 • Tons and tons of other UI-related XMLs • Drawables/colors + selectors • Themes + styles • Custom styling attributes
  4. Webuni webinar – @stewemetal How we write UI now? •

    View objects inflated from XML layout files • For more than 10 years now 🗓 • Tons and tons of other UI-related XMLs • Drawables/colors + selectors • Themes + styles • Custom styling attributes • View Binding / Data Binding • Code generation and a pinch of pixie dust… ✨ • @BindingAdapters
  5. Webuni webinar – @stewemetal Imperative 👑

  6. Webuni webinar – @stewemetal Imperative 👑 “Imperative programming focuses on

    describing how a program operates step by step, rather than on high-level descriptions of its expected results.” - Wikipedia
  7. Webuni webinar – @stewemetal Imperative 👑 • Objects created exactly

    how we declare them <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="60dp" android:paddingVertical="8dp" android:paddingHorizontal="16dp" > <TextView android:id="@+id/itemText" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginEnd="16dp" android:textSize="16sp" android:textColor="@color/grey" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/itemForwardArrow" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:text="Test view item" /> <ImageView android:id="@+id/itemForwardArrow" android:layout_width="40dp" android:layout_height="40dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" android:src="@drawable/ic_chevron_right" app:tint="@color/grey" /> </androidx.constraintlayout.widget.ConstraintLayout> inflate
  8. Webuni webinar – @stewemetal Declarative 📣 The Future (is now!)

  9. Webuni webinar – @stewemetal Declarative 📣 • ❌ Writing how

    the UI should be built • ✅ Writing what the UI should look like, and let the framework figure out how to achieve the result ConstraintLayout( modifier = Modifier .background(MaterialTheme.colors.background) .height(60.dp) .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 8.dp), ) { Text( text = item.text, color = Grey, ) Icon( painterResource(id = drawable.ic_chevron_right), ) } * Not actual working code! *
  10. Webuni webinar – @stewemetal What is Compose?

  11. Webuni webinar – @stewemetal What is Compose? • A general-purpose,

    multiplatform compiler and runtime for managing a tree of nodes of any type https://jakewharton.com/a-jetpack-compose-by-any-other-name/
  12. Webuni webinar – @stewemetal What is Compose? • A general-purpose,

    multiplatform compiler and runtime for managing a tree of nodes of any type AND ALSO • A UI toolkit and DSL to render app UI on Android and Desktop https://jakewharton.com/a-jetpack-compose-by-any-other-name/
  13. Webuni webinar – @stewemetal What is Compose UI? • Declarative

    • Like React (Native), Flutter, and SwiftUI • Component-based • Unbundled • Kotlin-only • The UI is a function of the current UI state
  14. Webuni webinar – @stewemetal • Declarative • Like React (Native),

    Flutter, and SwiftUI • Component-based • Unbundled • Kotlin-only • The UI is a function of the current UI state f(state) UI = What is Compose UI?
  15. Webuni webinar – @stewemetal • We can use Composable functions

    to describe UI components based on the current UI state • Two-way interoperability • Works with existing View-based UI and vice versa • AndroidView composable for Views in Compose • ComposeView for Composables in Views What is Compose UI? f(state) UI =
  16. Webuni webinar – @stewemetal A simple list – View vs.

    Compose Check out the first demo project here: https://github.com/stewemetal/android-view-vs-compose
  17. Webuni webinar – @stewemetal A simple list – Android View

  18. Webuni webinar – @stewemetal A simple list – Android View

    ConstraintLayout
  19. Webuni webinar – @stewemetal A simple list – Android View

    MaterialToolbar RecyclerView
  20. Webuni webinar – @stewemetal A simple list – Android View

    List item view… List item view…
  21. Webuni webinar – @stewemetal A simple list – Android View

    List item view <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="60dp" android:paddingVertical="8dp" android:paddingHorizontal="16dp“> <TextView android:id="@+id/itemText" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginEnd="16dp" android:textSize="16sp" android:textColor="@color/grey" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/itemForwardArrow" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:text="Test view item“/> <ImageView android:id="@+id/itemForwardArrow" android:layout_width="40dp" android:layout_height="40dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" tools:ignore="ContentDescription" android:src="@drawable/ic_chevron_right" app:tint="@color/grey“/> </androidx.constraintlayout.widget.ConstraintLayout> - layout_item.xml
  22. Webuni webinar – @stewemetal A simple list – Android View

    Layout of a Fragment - fragment_view.xml <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar" android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:title="View implementation" style="@style/Theme.ComposeDemo.AppBarOverlay"/> <androidx.recyclerview.widget.RecyclerView android:id="@+id/list" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/toolbar" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/> </androidx.constraintlayout.widget.ConstraintLayout>
  23. Webuni webinar – @stewemetal A simple list – Android View

    Fragment - ViewFragment.kt class ViewFragment : Fragment() { lateinit var adapter: ViewItemAdapter override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View = inflater.inflate(R.layout.fragment_view, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) adapter = ViewItemAdapter() val list = view.findViewById<RecyclerView>(R.id.list) list.adapter = adapter adapter.submitList( mutableListOf<Item>().apply { (1..30).forEach { add(Item("View item $it")) } } ) } }
  24. Webuni webinar – @stewemetal A simple list – Android View

    Adapter - ViewItemAdapter.kt class ViewItemAdapter : ListAdapter<Item, ItemViewHolder>(ItemDiffUtil()) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder = ItemViewHolder( LayoutInflater.from(parent.context) .inflate(R.layout.layout_item, parent, false) ) override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { holder.bind(getItem(position)) } inner class ItemViewHolder(itemView: View) : ViewHolder(itemView) { private val itemText: TextView = itemView.findViewById(R.id.itemText) fun bind(item: Item) { itemText.text = item.text } } } class ItemDiffUtil : DiffUtil.ItemCallback<Item>() { override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean { return oldItem == newItem } override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean { return oldItem == newItem } }
  25. Webuni webinar – @stewemetal A simple list – Jetpack Compose

  26. Webuni webinar – @stewemetal A simple list – Jetpack Compose

  27. Webuni webinar – @stewemetal A simple list – Jetpack Compose

    ComposeView (interoperability)
  28. Webuni webinar – @stewemetal A simple list – Jetpack Compose

    Composable Composable Composable
  29. Webuni webinar – @stewemetal Interoperability - fragment_compose.xml <?xml version="1.0" encoding="utf-8"?>

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.compose.ComposeFragment"> <androidx.compose.ui.platform.ComposeView android:id="@+id/composeView" android:layout_width="match_parent" android:layout_height="match_parent"/> </FrameLayout> A simple list – Jetpack Compose
  30. Webuni webinar – @stewemetal Interoperability - fragment_compose.xml A simple list

    – Jetpack Compose <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.compose.ComposeFragment"> <androidx.compose.ui.platform.ComposeView android:id="@+id/composeView" android:layout_width="match_parent" android:layout_height="match_parent"/> </FrameLayout> <androidx.compose.ui.platform.ComposeView android:id="@+id/composeView" android:layout_width="match_parent" android:layout_height="match_parent"/> Interoperability with Views
  31. Webuni webinar – @stewemetal A simple list – Jetpack Compose

    List item composable - ListItem.kt } @Composable fun ListItem( item: Item, ) { // ...
  32. Webuni webinar – @stewemetal A simple list – Jetpack Compose

    List item composable - ListItem.kt @Composable fun ListItem( item: Item, ) { // ... } @Composable fun ListItem( ) f()
  33. Webuni webinar – @stewemetal A simple list – Jetpack Compose

    List item composable - ListItem.kt } @Composable fun ListItem( item: Item, ) { // ... f(state) @Composable fun ListItem( item: Item, ) { // ... }
  34. Webuni webinar – @stewemetal A simple list – Jetpack Compose

    List item composable - ListItem.kt @Composable fun ListItem( item: Item, ) { // ... } f(state) UI = @Composable fun ListItem( item: Item, ) { // ... }
  35. Webuni webinar – @stewemetal A simple list – Jetpack Compose

    List item composable - ListItem.kt } @Composable fun ListItem( item: Item, ) { // ...
  36. Webuni webinar – @stewemetal @Composable fun ListItem( item: Item, )

    { ConstraintLayout( modifier = Modifier .background(MaterialTheme.colors.background) .height(60.dp) .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 8.dp), ) { // ... } A simple list – Jetpack Compose List item composable - ListItem.kt
  37. Webuni webinar – @stewemetal @Composable fun ListItem( item: Item, )

    { ConstraintLayout( modifier = Modifier .background(MaterialTheme.colors.background) .height(60.dp) .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 8.dp), ) { val (text, chevron) = createRefs() Text( text = item.text, color = Grey, fontSize = 16.sp, modifier = Modifier .constrainAs(text) { top.linkTo(parent.top) start.linkTo(parent.start) end.linkTo(chevron.start) bottom.linkTo(parent.bottom) width = Dimension.fillToConstraints }, ) Icon( painterResource(id = drawable.ic_chevron_right), tint = Grey, contentDescription = "Item arrow forward", modifier = Modifier .constrainAs(chevron) { top.linkTo(parent.top) start.linkTo(text.end, margin = 16.dp) end.linkTo(parent.end) bottom.linkTo(parent.bottom) width = Dimension.value(40.dp) height = Dimension.value(40.dp) }, ) } A simple list – Jetpack Compose List item composable - ListItem.kt
  38. Webuni webinar – @stewemetal @Composable fun ListItem( item: Item, )

    { ConstraintLayout( modifier = Modifier .background(MaterialTheme.colors.background) .height(60.dp) .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 8.dp), ) { val (text, chevron) = createRefs() Text( text = item.text, color = Grey, fontSize = 16.sp, modifier = Modifier .constrainAs(text) { top.linkTo(parent.top) start.linkTo(parent.start) end.linkTo(chevron.start) bottom.linkTo(parent.bottom) width = Dimension.fillToConstraints }, ) Icon( painterResource(id = R.drawable.ic_chevron_right), tint = Grey, contentDescription = "Item arrow forward", modifier = Modifier .constrainAs(chevron) { top.linkTo(parent.top) start.linkTo(text.end, margin = 16.dp) end.linkTo(parent.end) bottom.linkTo(parent.bottom) width = Dimension.value(40.dp) height = Dimension.value(40.dp) }, ) } A simple list – Jetpack Compose List item composable - ListItem.kt val (text, chevron) = createRefs() Text( text = item.text, color = Grey, fontSize = 16.sp, modifier = Modifier .constrainAs(text) { top.linkTo(parent.top) start.linkTo(parent.start) end.linkTo(chevron.start) bottom.linkTo(parent.bottom) width = Dimension.fillToConstraints }, )
  39. Webuni webinar – @stewemetal @Composable fun ComposeContent() { ComposeDemoTheme {

    Scaffold( topBar = { TopAppBar( title = { Text("Compose implementation") }, ) }, content = { LazyColumn { items((1..30).toList()) { ListItem(Item("Compose item $it")) } } }, ) } } A simple list – Jetpack Compose Content composable - ComposeContent.kt
  40. Webuni webinar – @stewemetal @Composable fun ComposeContent() { ComposeDemoTheme {

    Scaffold( topBar = { TopAppBar( title = { Text("Compose implementation") }, ) }, content = { LazyColumn { items((1..30).toList()) { ListItem(Item("Compose item $it")) } } }, ) } } A simple list – Jetpack Compose Content composable - ComposeContent.kt LazyColumn { items((1..30).toList()) { ListItem(Item("Compose item $it")) } } The equivalent of a RecyclerView + Adapter
  41. Webuni webinar – @stewemetal @Composable fun ComposeContent() { ComposeDemoTheme {

    Scaffold( topBar = { TopAppBar( title = { Text("Compose implementation") }, ) }, content = { LazyColumn { items((1..30).toList()) { ListItem(Item("Compose item $it")) } } }, ) } } A simple list – Jetpack Compose Content composable - ComposeContent.kt
  42. Webuni webinar – @stewemetal Fragment - ComposeFragment.kt class ComposeFragment :

    Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View = inflater.inflate(R.layout.fragment_compose, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) view.findViewById<ComposeView>(R.id.composeView) .apply { setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed) setContent { ComposeContent() } } } } A simple list – Jetpack Compose
  43. Webuni webinar – @stewemetal Fragment - ComposeFragment.kt A simple list

    – Jetpack Compose class ComposeFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View = inflater.inflate(R.layout.fragment_compose, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) view.findViewById<ComposeView>(R.id.composeView) .apply { setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed) setContent { ComposeContent() } } } } view.findViewById<ComposeView>(R.id.composeView) .apply { setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed) setContent { ComposeContent() } } Interoperability with Views
  44. Webuni webinar – @stewemetal A simple list – View vs.

    Compose • View • Imperative • 2 layout XMLs • Binding code in a Fragment • RecyclerView adapter • Compose • Declarative • 2 composable functions in Kotlin • 1 layout in XML (interop) • Interop code in a Fragment
  45. Webuni webinar – @stewemetal • Start here, please 🙏 Thinking

    in Compose https://developer.android.com/jetpack/compose/mental-model
  46. Webuni webinar – @stewemetal • UI components in Kotlin –

    as Composable functions • Themes in Kotlin – as Composable functions • and objects in the composition • Value resources in Kotlin • Drawables, strings, dimensions etc. can still come from the usual XML sources thanks to interop features • The UI is composed based on the current state and recomposed when a new state is provided Thinking in Compose UI
  47. Webuni webinar – @stewemetal • Initial composition • Calling Composables

    for the first time • Keeping track of the Composition Composition, recomposition https://developer.android.com/jetpack/compose/lifecycle
  48. Webuni webinar – @stewemetal • Initial composition • Calling Composables

    for the first time • Keeping track of the Composition • Recomposition • When the app state changes, Compose re-executes composables and updates the Composition Composition, recomposition https://developer.android.com/jetpack/compose/lifecycle
  49. Webuni webinar – @stewemetal • Initial composition • Calling Composables

    for the first time • Keeping track of the Composition • Recomposition • When the app state changes, Compose re-executes composables and updates the Composition Composition, recomposition https://developer.android.com/jetpack/compose/lifecycle
  50. Webuni webinar – @stewemetal • Use Composable functions to describe

    UI component hierarchies based on state data that can change over time UI state in Compose UI @Composable fun CustomButton( text: String, loading: Boolean, enabled: Boolean, onClick: () -> Unit, ) { . . . }
  51. Webuni webinar – @stewemetal • Use Composable functions to describe

    UI component hierarchies based on state data that can change over time CustomButton( // . . . loading = false, enabled = false, ) UI state in Compose UI Custom Button
  52. Webuni webinar – @stewemetal • Use Composable functions to describe

    UI component hierarchies based on state data that can change over time CustomButton( // . . . loading = false, enabled = true, ) UI state in Compose UI Custom Button
  53. Webuni webinar – @stewemetal • Use Composable functions to describe

    UI component hierarchies based on state data that can change over time CustomButton( // . . . loading = true, enabled = true, ) UI state in Compose UI
  54. Webuni webinar – @stewemetal A life-like project

  55. Webuni webinar – @stewemetal A life-like project (🐴)

  56. Webuni webinar – @stewemetal How does Compose fit into our

    lives (and code)? Let’s find out! https://github.com/stewemetal/composehydrationtracker A life-like project (🐴)
  57. Webuni webinar – @stewemetal Existing project? No problem! • Composables

    in existing views • Activities • Fragments • Views in a Composition • Two-way interoperability • Compose works well with existing View-based UI • We can reuse our existing (custom) UI elements
  58. Webuni webinar – @stewemetal Existing project? No problem! • Composables

    in existing views • Activities • Fragments • Views in a Composition • Two-way interoperability • Compose works well with existing View-based UI • We can reuse our existing (custom) UI elements • Migrate at your own pace and educate your team!
  59. Webuni webinar – @stewemetal 📚Resources for learning📚

  60. Webuni webinar – @stewemetal Resources • The official docs •

    https://developer.android.com/jetpack/compose/documentation • https://developer.android.com/jetpack/compose/mental-model • https://developer.android.com/jetpack/compose/state • https://developer.android.com/jetpack/compose/lifecycle • Android Developers videos • https://www.youtube.com/c/AndroidDevelopers/search?query=co mpose
  61. Webuni webinar – @stewemetal Resources • The Compose Runtime, Demystified

    • Leland Richardson, KotlinConf ‘19 • https://youtu.be/6BRlI5zfCCk
  62. Webuni webinar – @stewemetal Resources • Jetpack Compose internals –

    Jorge Castillo • https://jorgecastillo.dev/book/ • Pretty theory-heavy!
  63. Webuni webinar – @stewemetal Resources • Jetpack Compose basics codelab

    • https://developer.android.com/codelabs/jetpack- compose-basics
  64. Webuni webinar – @stewemetal Resources • Jetpack Compose Samples •

    https://github.com/android/compose-samples
  65. Webuni webinar – @stewemetal Resources • Android Developers Backstage (ADB)

    podcast • https://adbackstage.libsyn.com/ • Starting with EP 164 • https://adbackstage.libsyn.com/episode-164-jetpack- compose-compilation
  66. Webuni webinar – @stewemetal There’s so much more to learn

    about! • Compose lifecycle and how composition works • State handling • Side effects • Navigation • Animations • Accessibility • Custom theming • Integration testing • . . .
  67. Webuni webinar – @stewemetal stewemetal • Less code with the

    declarative approach • Faster iteration, faster implementation • Easier UI testing • Existing code/resources supported Photo by Xavi Cabrera The Future of Android Development István Juhos Jetpack Compose