Создание компонентов Composable

Последнее обновление: 30.03.2024

Основой пользовательского интерфейса в Jetpack Compose являются компоненты, которые оформлены в виде функции с аннотацией @Composable. При вызове @Composable-компонента обычно передаются некоторые данные и набор свойств, которые определяют отображение и поведение определенной части пользовательского интерфейса. По сути, функции @Composable преобразуют данные в элементы пользовательского интерфейса. Эти функции не возвращают значения в традиционном смысле функции Kotlin, а вместо этого определяют элементы пользовательского интерфейса для рендеринга.

@Composable-функции могут вызывать другие подобные компоненты для создания иерархии компонентов. При разработке приложений с помощью Compose мы можем как определять свои собственные компоненты, так и использовать встроенные. Собственно типичный пользовательский интерфейс на основе Compose обычно состоит из комбинации встроенных и кастомных @Composable-компонентов.

Встроенные компоненты, которые по умолчанию входят в состав Compose, можно разделить на три категории: Layout, Foundation и Material Design.

В категорию Layout входят компоненты, которые определяют макет приложения, позволяют определить расположение других компонентов на экране. В частности, это следуюшие компоненты:

  • Box

  • BoxWithConstraints

  • Column

  • ConstraintLayout

  • Row

Компоненты Foundation — это набор минимальных компонентов, которые обеспечивают базовую функциональность пользовательского интерфейса. Например, это:

  • BaseTextField

  • Canvas

  • Image

  • LazyColumn

  • LazyRow

  • Shape

  • Text

Компоненты Material Design разработаны так, чтобы они соответствовали рекомендациям визуальному стилю Google Material. Например, это:

  • AlertDialog

  • Button

  • Card

  • CircularProgressIndicator

  • DropdownMenu

  • Checkbox

  • FloatingActionButton

  • LinearProgressIndicator

  • ModalDrawer

  • RadioButton

  • Scaffold

  • Slider

  • Snackbar

  • Switch

  • TextField

  • TopAppBar

  • BottomNavigation

Создание своего компонента

Аналогично мы могли бы создать свой компонент на основе имеющихся и использовать его в качестве корневого компонента. Например, изменим код следующим образом:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.sp

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Hello()
        }
    }
}

@Composable
fun Hello() {
    Text(text = "Hello METANIT.COM!",
        style = TextStyle(
            fontSize = 28.sp
        )
    )
}

Здесь мы определили свой компонент Hello, внутри которого по сути используется встроенный компонент Text. Чтобы функция Hello рассматривалась как компонент, к ней применяется аннотация @Composable из пакета androidx.compose.runtime. Причем функции компонентов естественно можно вынести в отдельные файлы и даже пакеты и оттуда подключать.

Настройка предварительного просмотра

И также для наших компонентов можно задать предварительный просмотр в Android Studio, что позволяет уже до запуска увидеть, как будет выглядеть приложение. Так, изменим код MainActivity.kt следующим образом:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.sp

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Hello()
        }
    }
}

@Composable
fun Hello() {
    Text(text = "Hello METANIT.COM!",
        style = TextStyle(
            fontSize = 28.sp
        )
    )
}

@Preview(showSystemUi = true)
@Composable
fun HelloPreview() {
    Hello()
}

Здесь добавлен компонент HelloPreview, который использует компонент Hello. К компоненту HelloPreview применяется аннотация @Preview из пакета androidx.compose.ui.tooling.preview. Свойство showSystemUi = true этой аннотации позволяет увидеть интерфейс приложения в целом в области предпросмотра в Android Studio:

Предварительный просмотр Jetpack Compose в Android Studio

Механика функций Composable

Исходный код аннотации Composable выглядит следующим образом:

@MustBeDocumented
@Retention(AnnotationRetention.BINARY)
@Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.TYPE,
AnnotationTarget.TYPE_PARAMETER,
AnnotationTarget.PROPERTY_GETTER
)
annotation class Composable

Здесь мы видим, что к классу аннотации, в совю очередь, применяются три других аннотации:

  • @MustBeDocumented: указывает, что аннотация является частью общедоступного API и должна быть включена в создаваемую документацию.

  • @Retention: сообщает компилятору, как долго должна сохраняться аннотация. Значение AnnotationRetention.BINARY позволяет сохранить код в бинарном файле во время компиляции.

  • @Target: описывает контексты, в которых применяется этот тип. @Composable можно применять к типам, параметрам, функциям и свойствам.

Последняя аннотация указывает, что мы можем применять аннотацию @Composable гораздо шире, чем просто к функции верхнего уровня, как в примерах выше. Например:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Message("Hello METANIT.COM!").printMessage()
        }
    }
}

class Message(val messageText:String){
    @Composable
    fun printMessage(){
        Text(text = messageText)
    }
}

Здесь аннотация @Composable применяется к функции printMessage внутри класса Message. Эта функция определяет компонент Text, в который передается значение свойства messageText. То есть функция printMessage выступает в качестве компонента. И мы можем использовать этот компонент, вызывав данную функцию:

setContent {
    Message("Hello METANIT.COM!").printMessage()
}

Соответственно фактически интерфейс будет определяться компонентом Text, который выведет строку "Hello METANIT.COM!":

Аннотация Composable в Kotlin и Jetpack Compose

setContent

Теперь пришло время, чтобы сказать чуть подробнее, как устанавливается, корневой компонент. Это происходит в методе setContent. Сигнатура метода setContent() выглядит следующим образом:

fun ComponentActivity.setContent(
    parent: CompositionContext? = null,
    content: @Composable () -> Unit
) { ... }

Как можно видеть, setContent() является функцией расширения типа ComponentActivity. Функции расширения добавляют классу дополнительную функциональность без изменения его исходного кода. Это означает, что метод setContent() можно использовать для любого ComponentActivity или его подклассов, например AppCompatActivity.

Вызов setContent() устанавливает функцию из параметра content в качестве корневого компонента, который выступает в качестве контейнера для всех остальных компонентов и в который можно добавить произвольное количество других компонентов. Обратите внимание, что функция content также аннотируется с помощью аннотации @Composable. Так как аннотация @Target позволяет применять аннотацию @Composable к параметрам функции. Конкретно в данном случае аннотация @Composable помечает передаваемую параметру content функцию как Composable-функцию.

Еще один параметр метода setContent() представляет объект CompositionContext, который предствляет контекст композиции интерфейса и используется для рекомпозиции - обновления пользовательского интерфейса.

Компонент как параметр

Определение метода setContent демонстрирует один из распространенных способов передачи компонентов в другие компоненты. Подобная организация очень удобна, поскольку мы можем в функции получить компонент, как-то настроить его, выполнить с ним некоторые действия.

Например, определим следующее приложение:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.Text
import androidx.compose.ui.unit.sp

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Message{
                Text(text = "Hello World", fontSize = 28.sp)
            }
        }
    }
}
@Composable
fun Message(content: @Composable () -> Unit){ content()}

Здесь определен компонент Message, который через параметр принимает некоторую функцию-компонент. Внутри компонента Message мы просто вызываем переданную функцию.

Методе setContent вызываем компонент Message. А в функции, которая передается в Message через параметр content, вызываем компонент Text:

Message{
    Text(text = "Hello World", fontSize = 28.sp)
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850