Вы можете добавить интерфейс на основе Compose в существующее приложение, в котором используется дизайн на основе View.
Чтобы создать новый экран, полностью основанный на Compose, вызовите метод setContent() и передайте нужные composable-функции.
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!") }
Этот код выглядит так же, как и в приложении, созданном только с помощью Compose.
ViewCompositionStrategy на ComposeView
ViewCompositionStrategy определяет, когда композиция должна быть удалена. По умолчанию используется стратегия ViewCompositionStrategy.Default, которая удаляет композицию, когда базовый элемент ComposeView отсоединяется от окна, если только он не является частью контейнера пула, например RecyclerView. В приложении с одним Activity, использующем только Compose, такой режим работы по умолчанию является желательным, однако, если вы постепенно добавляете Compose в свою базу кода, это поведение может привести к потере состояния в некоторых сценариях.
Чтобы изменить ViewCompositionStrategy, вызовите метод setViewCompositionStrategy() и укажите другую стратегию.
В таблице ниже приведены различные сценарии использования ViewCompositionStrategy.
ViewCompositionStrategy |
Описание и сценарий взаимодействия |
|---|---|
DisposeOnDetachedFromWindow |
Композиция будет удалена, когда базовый ComposeView будет отсоединен от окна. Заменен на DisposeOnDetachedFromWindowOrReleasedFromPool.Сценарий взаимодействия: * ComposeView, независимо от того, является ли он единственным элементом в иерархии View или находится в контексте смешанного экрана View/Compose (не во фрагменте). |
DisposeOnDetachedFromWindowOrReleasedFromPool (по умолчанию) |
Похоже на DisposeOnDetachedFromWindow, когда композиция не находится в контейнере пула, например RecyclerView. Если он находится в контейнере пула, он будет удален, когда сам контейнер пула отсоединится от окна или когда элемент будет отброшен (т.е. когда пул будет заполнен).Сценарий взаимодействия: * ComposeView является ли он единственным элементом в иерархии View или в контексте смешанного экрана View/Compose (не во фрагменте).* ComposeView как элемент в контейнере пула, например RecyclerView. |
DisposeOnLifecycleDestroyed |
Композиция будет удалена, когда будет уничтожен предоставленный Lifecycle.Сценарий взаимодействия * ComposeView в представлении фрагмента. |
DisposeOnViewTreeLifecycleDestroyed |
Композиция будет удалена, когда будет уничтожен объект Lifecycle, принадлежащий объекту LifecycleOwner, который возвращает метод ViewTreeLifecycleOwner.get следующего окна, к которому прикреплен объект View.Сценарий взаимодействия: * ComposeView в объекте View фрагмента.* ComposeView в представлении, жизненный цикл которого пока неизвестен. |
ComposeView во фрагментах (переходный этап)
Если вы хотите добавить контент Compose UI во фрагмент или существующий макет View, используйте 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, задайте стратегию Composition, которая лучше всего подходит для хоста 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 UI. Текст "Hello Android!" отображается виджетом TextView. Текст "Hello Compose!" отображается текстовым элементом Compose.
Вы также можете включить 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>
Предварительный просмотр composable-функций в редакторе макетов
Вы также можете просматривать composable-функции в редакторе макетов для макета XML, содержащего ComposeView. Это позволит вам увидеть, как ваши composable-функции выглядят в смешанном макете Views и Compose.
Допустим, вы хотите показать в редакторе макетов следующую composable-функцию: Обратите внимание, что composable-функции, аннотированные с помощью @Preview, хорошо подходят для предварительного просмотра в редакторе макетов.
@Preview @Composable fun GreetingPreview() { Greeting(name = "Android") }
Чтобы показать эту composable-функцию, используйте атрибут tools:composableName и задайте в качестве его значения полное имя composable-функции, которую нужно показать в макете.
<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>
