Как использовать функцию "Написать" в представлениях

Вы можете добавить интерфейс на основе 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>

Composable-функция в редакторе макетов