Slide 1

Slide 1 text

Jetpack Compose Кирилл Розов@Android Broadcast Серебряная пуля Android UI

Slide 2

Slide 2 text

👉 Блоггер 👉 Android GDE 👉 Автор Telegram канала “Android Broadcast” 👉 Автор YouTube канала “Android Broadcast” Кирилл Розов @kirill_rozov @krlrozov

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

План 👉 Что не так с Android View 👉 Что такое Compose 👉 Каким образом Compose меняет разработку UI 👉 Будущее Compose 👉 Мультиплатформенный UI 4

Slide 5

Slide 5 text

layout/activity_hello_world.xml 5

Slide 6

Slide 6 text

HelloWorldActivity.kt class HelloWorldActivity : ComponentActivity(R.layout.activity_hello_world) { private lateinit var textView: TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) textView = findViewById(R.id.text) setGreeting("Android") } fun setGreeting(name: String) { textView.text = "Hello, $name" } } 6

Slide 7

Slide 7 text

class HelloWorldActivity : ComponentActivity(R.layout.activity_hello_world) { private lateinit var textView: TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) textView = findViewById(R.id.text) setGreeting("Android") } fun setGreeting(name: String) { textView.text = "Hello, $name" } } 7

Slide 8

Slide 8 text

Альтернативы findViewById() 👉 AndroidAnnotations 👉 ButterKnife 👉 Kotlin Synthetic 👉 Android DataBinding/ViewBinding 8

Slide 9

Slide 9 text

class HelloWorldActivity : ComponentActivity(R.layout.activity_hello_world) { private lateinit var textView: TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) textView = findViewById(R.id.text) setGreeting("Android") } fun setGreeting(name: String) { textView.text = "Hello, $name" } } 9

Slide 10

Slide 10 text

class HelloWorldActivity : 
 ComponentActivity(R.layout.activity_hello_world) { private val viewBinding: ActivityHelloWorldBinding 
 by viewBinding() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setGreeting("Android") } fun setGreeting(name: String) { viewBinding.text.text = "Hello, $name" } } 10

Slide 11

Slide 11 text

Android View - всё объекты

Slide 12

Slide 12 text

RelativeLayout FrameLayout LinearLayout ConstraintLayout Какой Layout самый оптимальный для экрана X?

Slide 13

Slide 13 text

Списки в Android RecyclerView RecyclerView.Adapter ViewHolder DiffUtil ItemAnimator LayoutManager 13

Slide 14

Slide 14 text

Как много Custom View вам приходилось создавать?

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

Типы собственных View 👉 Полностью свой View 👉 Расширение существующих 
 Обычно “закостылить” 👉 Собственный ViewGroup 👉 Группировка View в виджет 
 Например, виджет контакта 
 16

Slide 17

Slide 17 text

Особенности Android View 👉 Разные языки для вёрстки (XML) и бизнес логики (Kotlin/JAVA) 👉 View xранит состояние (set методы + атрибуты) 👉 Зависимость от платформы 👉 Высокая сложность создания собственных View 👉 Синхронное API с вызовами только с главного/UI потока 👉 Много кода в View. Очень много! 
 Класс View содержит 30000+ строк в Android 11.0 17

Slide 18

Slide 18 text

Декларативный UI под Android и iOS UI XML

Slide 19

Slide 19 text

Jetpack Compose

Slide 20

Slide 20 text

class HelloComposeActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Greeting("Android") } } } @Composable fun Greeting(name: String) { Text( text = "Hello $name!", style = MaterialTheme.typography.h5 ) } 20

Slide 21

Slide 21 text

class HelloComposeActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Greeting("Android") } } } @Composable fun Greeting(name: String) { Text( text = "Hello $name!", style = MaterialTheme.typography.h5 ) } 21

Slide 22

Slide 22 text

class HelloComposeActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Greeting("Android") } } } @Composable fun Greeting(name: String) { Text( text = "Hello $name!", style = MaterialTheme.typography.h5 ) } 22

Slide 23

Slide 23 text

Концепции Jetpack Compose 👉 Декларативный (описываете результат) 👉 Единый язык для логики и UI (Kotlin) 👉 Можно использовать всё чему вы привыкли в Kotlin 👉 Не зависит от платформы 👉 Асинхронное параллельное построение UI 23

Slide 24

Slide 24 text

Особенности Android View 👉 Разные языки для вёрстки и бизнес логики 👉 Хранит состояние 👉 Зависимость от платформы 👉 Высокая сложность создания собственных View 👉 Синхронное API с вызовами только с главного/UI потока 👉 Много кода в View. Очень много! 24

Slide 25

Slide 25 text

Динамический контент

Slide 26

Slide 26 text

@Composable fun MultiGreeting(name: String) { Column { repeat(10) { Greeting(name) } } } 26

Slide 27

Slide 27 text

Интерактивный режим в IDE

Slide 28

Slide 28 text

@Composable fun Counter() { var counter by rememberSaveable { mutableStateOf(0) } Surface(modifier = Modifier.fillMaxSize()) { Column( modifier = Modifier.padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Text( text = counter.toString(), style = MaterialTheme.typography.h4 ) Spacer(modifier = Modifier.height(8.dp)) Row { Button(onClick = { counter ++ }) { Text(text = "+") } Spacer(modifier = Modifier.width((8.dp))) Button( enabled = counter > 0, onClick = { counter -- } ) { Text(text = "-") } } } } } 28

Slide 29

Slide 29 text

Превью в Android Studio @Composable fun Counter() { var counter by rememberSaveable { mutableStateOf(0) } Surface(modifier = Modifier.fillMaxSize()) { Column( modifier = Modifier.padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Text( text = counter.toString(), style = MaterialTheme.typography.h4 ) Spacer(modifier = Modifier.height(8.dp)) Row { Button(onClick = { counter + + }) { Text(text = "+") } Spacer(modifier = Modifier.width((8.dp))) Button( enabled = counter > 0, onClick = { counter -- } ) { Text(text = "-") } } } } } 29 Compose Preview Android Studio Artic Fox Canary 15

Slide 30

Slide 30 text

Списки

Slide 31

Slide 31 text

Списки @Composable fun SimpleList(items: List) { LazyColumn { items(items.size) { i - > SimpleTextItem(items[i].toString()) } } } @Composable fun SimpleTextItem(text: String) { Text( text = text, style = MaterialTheme.typography.h5, modifier = Modifier.padding(8.dp) ) } 31

Slide 32

Slide 32 text

Списки @Composable fun SimpleList(items: List) { LazyColumn { items(items.size) { i - > SimpleTextItem(items[i].toString()) } } } @Composable fun SimpleTextItem(text: String) { Text( text = text, style = MaterialTheme.typography.h5, modifier = Modifier.padding(8.dp) ) } 32

Slide 33

Slide 33 text

Списки @Composable fun SimpleList(items: List) { LazyColumn { items(items.size) { i - > SimpleTextItem(items[i].toString()) } } } @Composable fun SimpleTextItem(text: String) { Text( text = text, style = MaterialTheme.typography.h5, modifier = Modifier.padding(8.dp) ) } 33

Slide 34

Slide 34 text

Списки @Composable fun SimpleList(items: List) { LazyColumn { items(items.size) { i - > SimpleTextItem(items[i].toString()) } } } @Composable fun SimpleTextItem(text: String) { Text( text = text, style = MaterialTheme.typography.h5, modifier = Modifier.padding(8.dp) ) } 34

Slide 35

Slide 35 text

Списки @Composable fun SimpleList(items: List) { LazyColumn { items(items.size) { i - > SimpleTextItem(items[i].toString()) } } } @Composable fun SimpleTextItem(text: String) { Text( text = text, style = MaterialTheme.typography.h5, modifier = Modifier.padding(8.dp) ) } 35

Slide 36

Slide 36 text

Будущее Jetpack Compose

Slide 37

Slide 37 text

Compose Android, Desktop & Web

Slide 38

Slide 38 text

Jetpack Compose @Composable fun Counter() { var counter by rememberSaveable { mutableStateOf(0) } Surface(modifier = Modifier.fillMaxSize()) { Column( modifier = Modifier.padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Text( text = counter.toString(), style = MaterialTheme.typography.h4 ) Spacer(modifier = Modifier.height(8.dp)) Row { Button(onClick = { counter + + }) { Text(text = "+") } Spacer(modifier = Modifier.width((8.dp))) Button( enabled = counter > 0, onClick = { counter -- } ) { Text(text = "-") } } } } } 38

Slide 39

Slide 39 text

Compose for Desktop Window(title = "Compose Desktop") { MaterialTheme { Counter() } } 39

Slide 40

Slide 40 text

40

Slide 41

Slide 41 text

Где поддержка iOS?

Slide 42

Slide 42 text

Производительность

Slide 43

Slide 43 text

Производительность 👉 Не нужно парсить XML 👉 Оптимизация кода R8/ProGuard 👉 Асинхронное и многопоточное построение UI 43

Slide 44

Slide 44 text

Как изменится Android разработка

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

Fragment больше не нужен

Slide 47

Slide 47 text

Декомпозиция функционала 👉 Activity Result API 👉 OnBackPressedDispatcher 👉 SavedStateRegistry 👉 ViewModel 👉 Lifecycle 👉 ViewTreeLifecycleOwner 👉 ViewTreeViewModelStoreOwner 👉 ViewTreeSavedStateRegistryOwner 47

Slide 48

Slide 48 text

Библиотеки Jetpack с поддержкой Compose 👉 Navigation 👉 Activity 👉 Paging 3 👉 ViewModel 👉 Hilt 👉 Material 48

Slide 49

Slide 49 text

Недостатки Jetpack Compose ⛔ Поддерживает только Kotlin (а плохо ли?) ⛔ Нужно использовать Kotlin Compiler Backend IR (Kotlin 1.4.30 или новее) ⛔ Поддерживается только в Android Studio Canary ⛔ Не все виджеты есть в Compose (возможно уже неактуально) ⛔ Не является нативным UI, а лишь его имитация (аля Flutter) ⛔ Над производительностью еще работают ⛔ Над тулингом еще работают (но уже много достигнуто) ⛔ minSdk=21 (Android 5.0) 49

Slide 50

Slide 50 text

Преимущества Jetpack Compose ✅ minSdk=21 (Android 5.0) ✅ Один язык для всего ✅ Используйте ко мне чему привыкли в Kotlin. Например, Coroutine ✅ Не стоит заботиться о вложенности ✅ Возможность оптимизировать UI код (R8/ProGuard) ✅ Независимость от Android фреймворка ✅ Мультиплатформенное решение 50

Slide 51

Slide 51 text

Jetpack Compose 1.0 ИЮЛЬ 2021

Slide 52

Slide 52 text

Спасибо за внимание