Использование Compose в представлениях

Вы можете добавить пользовательский интерфейс на основе Compose в существующее приложение, использующее дизайн на основе представления.

Чтобы создать новый экран, полностью основанный на Compose, ваша активность вызывает метод setContent() и передает любые компонуемые функции, которые вам нравятся.

class ExampleActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent { // In here, we can call composables!
            MaterialTheme {
                Greeting(name = "compose")
            }
        }
    }
}

@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
}

Этот код выглядит так же, как в приложении, предназначенном только для создания сообщений.

ViewCompositionStrategy для ComposeView

ViewCompositionStrategy определяет, когда композиция должна быть удалена. По умолчанию ViewCompositionStrategy.Default удаляет композицию, когда базовый ComposeView отделяется от окна, если только он не является частью контейнера объединения, такого как RecyclerView . В приложении только с одним действием Compose такое поведение по умолчанию — это то, что вам нужно, однако если вы постепенно добавляете Compose в свою кодовую базу, это поведение может привести к потере состояния в некоторых сценариях.

Чтобы изменить ViewCompositionStrategy , вызовите метод setViewCompositionStrategy() и укажите другую стратегию.

В таблице ниже приведены различные сценарии, в которых можно использовать ViewCompositionStrategy :

ViewCompositionStrategy Описание и сценарий взаимодействия
DisposeOnDetachedFromWindow Композиция будет удалена, когда базовый ComposeView будет отсоединен от окна. С тех пор был заменен DisposeOnDetachedFromWindowOrReleasedFromPool .

Сценарий взаимодействия:

* ComposeView , является ли он единственным элементом в иерархии представления или в контексте смешанного экрана просмотра/составления (не во фрагменте).
DisposeOnDetachedFromWindowOrReleasedFromPool ( по умолчанию ) Аналогично DisposeOnDetachedFromWindow , когда композиция не находится в контейнере пула, таком как RecyclerView . Если он находится в контейнере-пуле, он будет удален, когда сам контейнер-пул отсоединяется от окна или когда элемент выбрасывается (т. е. когда пул заполнен).

Сценарий взаимодействия:

* ComposeView , является ли он единственным элементом в иерархии представления или в контексте смешанного экрана просмотра/составления (не во фрагменте).
* ComposeView как элемент в контейнере пула, таком как RecyclerView .
DisposeOnLifecycleDestroyed Композиция будет удалена, когда предоставленный Lifecycle будет уничтожен.

Сценарий взаимодействия

* ComposeView в представлении фрагмента.
DisposeOnViewTreeLifecycleDestroyed Композиция будет удалена, когда Lifecycle , принадлежащий LifecycleOwner возвращенный ViewTreeLifecycleOwner.get следующего окна, к которому прикреплено представление, будет уничтожен.

Сценарий взаимодействия:

* ComposeView в представлении фрагмента.
* ComposeView в представлении, жизненный цикл которого еще неизвестен.

ComposeView во фрагментах

Если вы хотите включить содержимое пользовательского интерфейса Compose во фрагмент или существующий макет представления, используйте ComposeView и вызовите его метод setContent() . ComposeView — это View Android.

Вы можете поместить ComposeView в свой XML-макет, как и любое другое View :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <TextView
      android:id="@+id/text"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content" />

  <androidx.compose.ui.platform.ComposeView
      android:id="@+id/compose_view"
      android:layout_width="match_parent"
      android:layout_height="match_parent" />
</LinearLayout>

В исходном коде Kotlin разверните макет из ресурса макета, определенного в XML. Затем получите ComposeView используя идентификатор XML, установите стратегию композиции, которая лучше всего подходит для хоста View , и вызовите setContent() чтобы использовать Compose.

class ExampleFragmentXml : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        val view = inflater.inflate(R.layout.fragment_example, container, false)
        val composeView = view.findViewById<ComposeView>(R.id.compose_view)
        composeView.apply {
            // Dispose of the Composition when the view's LifecycleOwner
            // is destroyed
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                // In Compose world
                MaterialTheme {
                    Text("Hello Compose!")
                }
            }
        }
        return view
    }
}

Альтернативно вы также можете использовать привязку представления для получения ссылок на ComposeView ссылаясь на сгенерированный класс привязки для вашего XML-файла макета:

class ExampleFragment : Fragment() {

    private var _binding: FragmentExampleBinding? = null

    // This property is only valid between onCreateView and onDestroyView.
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentExampleBinding.inflate(inflater, container, false)
        val view = binding.root
        binding.composeView.apply {
            // Dispose of the Composition when the view's LifecycleOwner
            // is destroyed
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                // In Compose world
                MaterialTheme {
                    Text("Hello Compose!")
                }
            }
        }
        return view
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

Два немного разных текстовых элемента, один над другим

Рисунок 1. Здесь показаны выходные данные кода, который добавляет элементы Compose в иерархию пользовательского интерфейса View. «Привет, Android!» текст отображается виджетом TextView . «Привет, напишите!» текст отображается с помощью элемента «Создать текст».

Вы также можете включить ComposeView непосредственно во фрагмент, если ваш полноэкранный режим создан с помощью Compose, что позволяет полностью избежать использования файла макета XML.

class ExampleFragmentNoXml : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return ComposeView(requireContext()).apply {
            // Dispose of the Composition when the view's LifecycleOwner
            // is destroyed
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                MaterialTheme {
                    // In Compose world
                    Text("Hello Compose!")
                }
            }
        }
    }
}

Несколько экземпляров ComposeView в одном макете

Если в одном макете имеется несколько элементов ComposeView , каждый из них должен иметь уникальный идентификатор, чтобы savedInstanceState работал.

class ExampleFragmentMultipleComposeView : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View = LinearLayout(requireContext()).apply {
        addView(
            ComposeView(requireContext()).apply {
                setViewCompositionStrategy(
                    ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
                )
                id = R.id.compose_view_x
                // ...
            }
        )
        addView(TextView(requireContext()))
        addView(
            ComposeView(requireContext()).apply {
                setViewCompositionStrategy(
                    ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
                )
                id = R.id.compose_view_y
                // ...
            }
        )
    }
}

Идентификаторы ComposeView определены в файле res/values/ids.xml :

<resources>
  <item name="compose_view_x" type="id" />
  <item name="compose_view_y" type="id" />
</resources>

Предварительный просмотр составных элементов в редакторе макетов

Вы также можете просмотреть составные элементы в редакторе макетов для вашего XML-макета, содержащего ComposeView . Это позволит вам увидеть, как ваши составные элементы выглядят в смешанном макете Views и Compose.

Допустим, вы хотите отобразить следующий составной элемент в редакторе макетов. Обратите внимание, что составные элементы, помеченные @Preview являются хорошими кандидатами для предварительного просмотра в редакторе макетов.

@Preview
@Composable
fun GreetingPreview() {
    Greeting(name = "Android")
}

Чтобы отобразить этот составной объект, используйте атрибут инструментов tools:composableName и установите его значение в полное имя составного объекта для предварительного просмотра в макете.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <androidx.compose.ui.platform.ComposeView
      android:id="@+id/my_compose_view"
      tools:composableName="com.example.compose.snippets.interop.InteroperabilityAPIsSnippetsKt.GreetingPreview"
      android:layout_height="match_parent"
      android:layout_width="match_parent"/>

</LinearLayout>

Composable отображается в редакторе макетов

Следующие шаги

Теперь, когда вы знаете API-интерфейсы совместимости для использования Compose в представлениях, узнайте, как использовать представления в Compose .

{% дословно %} {% дословно %} {% дословно %} {% дословно %}