Магия совместимости XML и Jetpack Compose

Магия совместимости XML и Jetpack Compose

Бэкендер


Я познакомился с Jetpack Compose на начальном этапе его разработки. Это было на заре 2020 года, и уже тогда я был впечатлен новым способом реализации UI. 

К сожалению, на тот момент в большинстве функциональностей отсутствовали обработка обратного стека, выпадающее меню, навигация и т.д. Но все это дела уже минувших дней, а мы возвращаемся назад в будущее — в 2023 год. Наконец-то Jetpack Compose приведен в состояние рабочей готовности. Он предоставляет отличные API совместимости, как никогда упрощая процесс перехода.

Преимущества Jetpack Compose

Jetpack Compose — современный набор инструментов для создания UI с рядом преимуществ. Особенно отметим возможность перехода на один язык программирования для всего приложения, что позволяет избавиться от XML-файлов.

Он облегчил и ускорил разработку UI за счет значительного сокращения шаблонного кода, который использовался в императивном UI для поиска представлений и настройки логики UI, поскольку логика в декларативном UI встроена внутри компонентов. Помимо этого, Jetpack Compose сильно упрощает создание анимации с состояниями, значения которых можно легко менять в среде выполнения.

Кроме того, навигация с Jetpack Compose становится намного удобнее, поскольку установка всех правил внутри XML-файлов сопровождалась чрезвычайными сложностями. Еще одно преимущество заключается в повышении производительности при запуске View, так как макеты компилируются подобно остальной части приложения и не требуют расширения. После полного перехода на Compose уменьшаются как размер приложения, так и время сборки.

С чего начать?

Уже используемый UI на основе представлений не требует сразу полной перезаписи. Обеспечиваемая поддержка совместимости позволяет Compose и View сосуществовать в базе кода. Благодаря этому вы можете добавлять новые функциональности с помощью Compose или переносить в него небольшие имеющиеся компоненты. По мере готовности и принятия Compose вы воспользуетесь рекомендуемой стратегией перехода, называемой восходящим подходом. Он основывается на поочередной перезаписи небольших представлений с экрана до того момента, пока вы не сможете перенести ViewGroup и RootView в Compose.

В следующих разделах вы узнаете:

  • как настроить среду для Compose; 
  • какие возможны сценарии;
  • как реализовать Compose во View и View в Compose;
  • как обрабатывать фрагменты Fragment в Compose.

Настройка Gradle 

В buildFeatures активируем Compose и ViewBinding, затем добавляем основные зависимости Compose:

android {
    ...
    buildFeatures {
        compose true
        viewBinding true
    }
    composeOptions {
        kotlinCompilerExtensionVersion '1.3.2'
    }
}
dependencies {
    ...
    implementation "androidx.activity:activity-compose:$version"
    implementation "androidx.compose.runtime:runtime-livedata:$version"
    implementation "androidx.compose.ui:ui:$version"
    implementation "androidx.compose.ui:ui-tooling-preview:$version"
    implementation "androidx.compose.material:material:$version"
    implementation "androidx.compose.ui:ui-viewbinding:$version"
    implementation "androidx.navigation:navigation-compose:$version"
}

Compose в View

  • ComposeView в макете XML. Добавляем ComposeView в макет XML:
<androidx.compose.ui.platform.ComposeView
    android:id="@+id/composeView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

Получаем доступ в Compose из макета XML: 

binding.composeView.apply {
    setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
    setContent {
        MaterialTheme {
            Text(text = "Hello in Compose World!")
        }
    }
}
  • ComposeView непосредственно как макет. Реализуем ComposeView как представление виджета и получаем доступ в Compose: 
override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View = ComposeView(requireContext()).apply {
    setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
    setContent {
        MaterialTheme {
            Text(text = "Hello in Compose World!")
        }
    }
}

ViewCompositionStrategy для ComposeView

Существуют 4 стратегии композиции представлений для ComposeView. По умолчанию Compose удаляет композицию каждый раз, когда представление отсоединяется от окна. Однако в некоторых случаях применение этого значения по умолчанию не рекомендовано: 

  • DisposeOnDetachedFromWindow;
  • DisposeOnDetachedFromWindowOrReleasedFromPool (по умолчанию);
  • DisposeOnLifecycleDestroyed;
  • DisposeOnViewTreeLifecycleDestroyed (Fragment, Transition, пользовательские представления с учетом Lifecycle). 

View в Compose

  • AndroidView для пользовательских представлений. Создаем пользовательское представление в factory, а также настраиваем слушателей события клика и обратные вызовы для взаимодействия View -> Compose. Затем при обновлении настраиваем взаимодействие для Compose -> View:
@Composable
fun XMLCounter() {
    var resultState by remember { mutableStateOf(0) }
    AndroidView(
        factory = { context ->
            CounterView(context).apply {
                // Пример взаимодействия View -> Compose 
                resultCallback = {
                    resultState = it
                }
            }
        },
        update = { counterView ->
            // Пример взаимодействия Compose -> View 
            counterView.binding.result.text = resultState.toString()
        }
    )
  • AndroidViewBinding. Расширяем ViewBinding в Compose и получаем доступ к классу Binding:
@Composable
fun XMLCounterBinding() {
    AndroidViewBinding(ComponentCounterBinding::inflate) {
        down.setOnClickListener { /*...*/ }
        up.setOnClickListener { /*...*/ }
    }
}

Fragment в Compose

Добавляем FragmentContainerView в макет XML:

<androidx.fragment.app.FragmentContainerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:name="com.artf.composeinview.ui.main.MainFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Получаем доступ к Fragment из Compose: 

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
        MaterialTheme {
            AndroidViewBinding(ActivityMainBinding::inflate) {
                val fragment = container.getFragment<MainFragment>()
                //...
            }
        }
    }
}

Заключение 

Отбросьте все сомнения и смело начинайте работать с Compose, поскольку в настоящее время API совместимости отлично функционируют. Они позволяют Compose и View сосуществовать в базе кода и предоставляют много преимуществ, включая оптимизацию этапа запуска, размер приложения и время сборки. 


Источник: https://nuancesprog.ru/p/16999/


Report Page