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

Android Jetpack Compose

916b2b50d1c958f9ed1c008623065b8a?s=47 MOSDROID
September 13, 2019

Android Jetpack Compose

Антон Зинаков, Mail.Ru Group at #MOSDROID 19 Potassium [in KasperskyLab HeadQuarter]

На текущий момент основной способ написания UI в Android — XML разметка. Такой подход тянется уже около 10 лет и имеет свои проблемы: совместимость с предыдущими версиями ОС, достаточный объем legacy в самих классах View, относительно сложно переиспользовать написанные элементы.

В докладе я расскажу про опыт использования Jetpack Compose представленного на I/O 2019 – декларативный фреймворк для создания UI с использованием Kotlin. Это большой шаг вперед и огромный задел на будущее Android: Jetpack Compose во многом решает проблемы сегодняшнего UI, упрощает и вносит современный подход в разработку.

916b2b50d1c958f9ed1c008623065b8a?s=128

MOSDROID

September 13, 2019
Tweet

Transcript

  1. Jetpack Compose

  2. User Interface today • XML & View

  3. User Interface today • XML & View • 2009

  4. None
  5. None
  6. None
  7. User Interface today • XML & View • 2009 •

    Compatibility problems
  8. None
  9. None
  10. User Interface today • XML & View • 2009 •

    Compatibility problems • Hard to display the same UI on different devices and Android versions
  11. User Interface today • XML & View • 2009 •

    Compatibility problems • Hard to display the same UI on different devices and Android versions • Reusability is not convenient
  12. Alternative UI frameworks

  13. Alternative UI frameworks

  14. Alternative UI frameworks

  15. Jetpack Compose

  16. Jetpack Compose • Declarative

  17. Jetpack Compose • Declarative • Kotlin

  18. Jetpack Compose • Declarative • Kotlin • Compatibility with different

    Android versions
  19. Jetpack Compose • Declarative • Kotlin • Compatibility with different

    Android versions • Easy to reuse
  20. Jetpack Compose • Declarative • Kotlin • Compatibility with different

    Android versions • Easy to reuse • Modern
  21. Let’s try it!

  22. Install REPO mkdir ~/bin PATH=~/bin:$PATH curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo chmod

    a+x ~/bin/repo
  23. Configure GIT git config --global user.name "Your Name" git config

    --global user.email "you@example.com"
  24. Create directory for checkout mkdir androidx-master-dev cd androidx-master-dev

  25. Initialize the repository repo init -u https://android.googlesource.com/platform/manifest -b androidx-master-dev

  26. Download the code (about 6GB) repo sync -j8 -c

  27. Change directory path/to/compose/frameworks/support/ui/

  28. and run… ./studiow

  29. How to print text? fun main() { print("Hello, Mosdroid!") }

  30. How to draw text on Android UI? @Composable fun Greeting()

    { Text(text = "Hello, Mosdroid!") }
  31. class MosdroidActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) setContent { Greeting() } } @Composable fun Greeting() { Text(text = "Hello, Mosdroid!") } }
  32. @Composable fun Greeting() { Text(text = "Hello, Mosdroid!") }

  33. @Composable fun Greeting() { Text( text = "Hello, Mosdroid!", style

    = TextStyle(fontSize = 48.sp) ) }
  34. @Composable fun Greeting() { Align(Alignment.Center) { Text( text = "Hello,

    Mosdroid!", style = TextStyle(fontSize = 48.sp) ) } }
  35. Real example • I want to create an application with

    possibility to add persons and display added person in list • I want to separate UI elements to two independent widgets
  36. Dummy implementation @Composable fun PersonEditorWidget() { Column { TextField(EditorModel()) Button(text

    = "Add Person") } }
  37. Dummy implementation @Composable fun PersonEditorWidget() { Column { TextField(EditorModel()) Button(text

    = "Add Person") } }
  38. data class PersonEditorModel( val name: String = "", val buttonTitle:

    String = "Add Person", val onClick: (String) -> Unit )
  39. data class PersonEditorModel( val name: String = "", val buttonTitle:

    String = "Add Person", val onClick: (String) -> Unit ) @Composable fun PersonEditorWidget(model: PersonEditorModel) { Column { val nameState = +state { EditorModel(model.name) } TextField( value = nameState.value, onValueChange = { nameState.value = it }, editorStyle = EditorStyle( textStyle = TextStyle(fontSize = 24.sp) ) ) Button( text = model.buttonTitle, onClick = { model.onClick(nameState.value.text) } ) } }
  40. @Composable fun PersonEditorWidget(model: PersonEditorModel) { Column { val nameState =

    +state { EditorModel(model.name) } TextField( value = nameState.value, onValueChange = { nameState.value = it }, editorStyle = EditorStyle( textStyle = TextStyle(fontSize = 24.sp) ) ) Button( text = model.buttonTitle, onClick = { model.onClick(nameState.value.text) } ) } } Padding(16.dp) { }
  41. @Composable fun PersonEditorWidget(model: PersonEditorModel) { Padding(16.dp) { Column { val

    nameState = +state { EditorModel(model.name) } TextField( value = nameState.value, onValueChange = { nameState.value = it }, editorStyle = EditorStyle( textStyle = TextStyle(fontSize = 24.sp) ) ) Button( text = model.buttonTitle, onClick = { model.onClick(nameState.value.text) } ) } } } FlexRow { expanded(1.0f) { } }
  42. @Composable fun PersonEditorWidget(model: PersonEditorModel) { Padding(16.dp) { Column { val

    nameState = +state { EditorModel(model.name) } TextField( value = nameState.value, onValueChange = { nameState.value = it }, editorStyle = EditorStyle( textStyle = TextStyle(fontSize = 24.sp) ) ) HeightSpacer(12.dp) FlexRow { expanded(1.0f) { Button( text = model.buttonTitle, onClick = { model.onClick(nameState.value.text) } ) } } }
  43. @Composable fun PersonListWidget(persons: List<Person>) { Column { persons.forEachIndexed { index,

    person -> Align(Alignment.TopLeft) { Padding(16.dp) { Text( text = person.name, style = TextStyle(fontSize = 18.sp) ) } } } } }
  44. @Composable fun PersonListWidget(persons: List<Person>) { val dividerColor = Color(0xFFC6C6C6) Column

    { persons.forEachIndexed { index, person -> Align(Alignment.TopLeft) { Padding(16.dp) { Text( text = person.name, style = TextStyle(fontSize = 18.sp) ) } } if (index != persons.lastIndex) { Divider( color = dividerColor, height = 0.5.dp ) } } } }
  45. @Composable fun Dashboard(persons: MutableList<Person>) { val state = +state {

    persons } Column { PersonEditorWidget( PersonEditorModel( name = "Anton", onClick = { state.value = state.value.apply { add(Person(it)) } } )) Divider() PersonListWidget(state.value) } }
  46. Add theme

  47. private val colorGreen = Color(0xFF1B5E20.toInt()) @Composable fun MosdroidTheme(@Children children: @Composable()

    () -> Unit) { val colors = MaterialColors(primary = colorGreen) val typography = MaterialTypography( h5 = TextStyle( fontFamily = FontFamily("Roboto"), fontWeight = FontWeight.w700, fontSize = 24.sp ), h6 = TextStyle( fontFamily = FontFamily("Roboto"), fontWeight = FontWeight.w700, fontSize = 18.sp ), button = TextStyle( fontFamily = FontFamily("Roboto"), fontWeight = FontWeight.w800, fontSize = 14.sp ) ) MaterialTheme(colors = colors, typography = typography) { children() } }
  48. override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MosdroidTheme {

    Dashboard(personList) } } }
  49. @Composable fun PersonEditorWidget(model: PersonEditorModel) { Padding(16.dp) { Column { val

    nameState = +state { EditorModel(model.name) } TextField( value = nameState.value, onValueChange = { nameState.value = it }, editorStyle = EditorStyle( textStyle = TextStyle(fontSize = 24.sp) ) ) HeightSpacer(12.dp) FlexRow { expanded(1.0f) { Button( text = model.buttonTitle, onClick = { model.onClick(nameState.value.text) } ) } } }
  50. @Composable fun PersonEditorWidget(model: PersonEditorModel) { Padding(16.dp) { Column { val

    nameState = +state { EditorModel(model.name) } TextField( value = nameState.value, onValueChange = { nameState.value = it }, editorStyle = EditorStyle( textStyle = +themeTextStyle { h5 } ) ) HeightSpacer(12.dp) FlexRow { expanded(1.0f) { Button( text = model.buttonTitle, onClick = { model.onClick(nameState.value.text) } ) } } }
  51. None
  52. None
  53. Didn’t covered • Animations • Complex layout • Internal principles

  54. Links • Official resource: https://developer.android.com/jetpack/compose • Jetpack Compose Principles: http://intelligiblebabble.com/compose-from-

    first-principles/ • Example: https://medium.com/q42-engineering/android-jetpack- compose-895b7fd04bf4 • Compose Readme: https://android.googlesource.com/platform/ frameworks/support/+/refs/heads/androidx-master-dev/ui/README.md
  55. Conclusion • pre-alpha

  56. Conclusion • pre-alpha • No good instruments

  57. Conclusion • pre-alpha • No good instruments • UI tests?

  58. Conclusion • pre-alpha • No good instruments • UI tests?

    • Functions - small UI components
  59. Conclusion • pre-alpha • No good instruments • UI tests?

    • Functions - small UI components • Android version independent
  60. Conclusion • pre-alpha • No good instruments • UI tests?

    • Functions - small UI components • Android version independent • Less boilerplate
  61. Questions? Zinakov Anton Android Developer ab.zinakov@gmail.com @rygital