Connect with us

Разработка

Выразительные анимации для всех

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

Опубликовано

/

     
     

Недавно Google анонсировала следующую итерацию своего языка Material Design — Material 3 Expressive.

Как объясняют в Google, они хотят привнести индивидуальность и эмоции в скучные пользовательские интерфейсы приложений, помогая пользователям чувствовать себя более связанными со своими смартфонами, которые, по словам Google, больше не являются простыми инструментами, а являются продолжением их самих.

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

Выразительные анимации для всех

По мере изучения возможностей Material 3 Expressive одним из первых компонентов, которые я собираюсь внедрить, станут кнопки-переключатели. Благодаря новой выразительной системе движений и визуальных эффектов мне не терпится увидеть, как эти кнопки могут стать более динамичными, плавными и эмоционально привлекательными — выйти за рамки статичных элементов пользовательского интерфейса и стать тем, что действительно реагирует на взаимодействие с пользователем.

Давайте начнем!

Для начала создадим 3 кнопки, как в превью Google, но сделаем это пошагово.

var selectedIndex by remember { mutableIntStateOf(-1) }
val selectedColor = Color(0xFF554F6E)
val unselectedColor = Color(0xFFEAE5FF)
val icons = listOf(Icons.Outlined.Alarm, Icons.Outlined.LinkOff, Icons.Outlined.Wifi)
val iconSize = 60.dp

Row(Modifier.padding(horizontal = 8.dp),) {
  icons.forEachIndexed { index, icon ->
    FloatingActionButton(
      modifier = Modifier
          .padding(4.dp)
          .width(
            when (index) {
              1 -> iconSize / 1.5f
              2 -> iconSize * 1.5f
              else -> iconSize
            }
          )
          .height(iconSize),
      elevation = FloatingActionButtonDefaults.elevation(
        defaultElevation = 0.dp,
        pressedElevation = 0.dp
       ),
       containerColor = if (selectedIndex == index) {
         selectedColor
       } else {
         unselectedColor
       },
       contentColor = if (selectedIndex == index) {
         unselectedColor
       } else {
         selectedColor
       },
       onClick = { selectedIndex = index },
     ) {
        Icon(
          imageVector = icon,
          contentDescription = null,
        )
    }
  }
}

Вот что мы получим в результате. Давайте сделаем так, чтобы это соответствовало примеру Google с изменением размеров, просто используя обычный размер Floating Action Button.

Выразительные анимации для всех

Теперь у нас есть все необходимое, не хватает только одной вещи. Давайте добавим немного анимации и сделаем интерфейс живым.

@Composable
fun ExpressiveButtonAnimation() {
    var selectedIndex by remember { mutableIntStateOf(-1) }
    val checkedColor = Color(0xFF554F6E)
    val uncheckedColor = Color(0xFFEAE5FF)

    val icons = listOf(Icons.Outlined.Alarm, Icons.Outlined.LinkOff, Icons.Outlined.Wifi)
    var weights = remember { mutableStateListOf(0.85f, 0.65f, 1.5f) }

    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color(0xFFDAD2FF)),
        contentAlignment = Alignment.Center,
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth(0.6f)
                .padding(horizontal = 8.dp)
        ) {
            icons.forEachIndexed { index, icon ->
                ExpressiveFloatingActionButton(
                    icon = icon,
                    itemWeight = weights[index],
                    checked = selectedIndex == index,
                    checkedColor = checkedColor,
                    uncheckedColor = uncheckedColor,
                    onClick = {
                        selectedIndex = if (index == selectedIndex) {
                            -1
                        } else {
                            index
                        }
                    }
                )
            }
        }
    }
}

@Composable
fun RowScope.ExpressiveFloatingActionButton(
    icon: ImageVector,
    itemWeight: Float,
    checked: Boolean,
    checkedColor: Color,
    uncheckedColor: Color,
    onClick: () -> Unit,
) {
    var shapeSelected by remember { mutableStateOf(false) }
    val animatedRadius by animateDpAsState(
        targetValue = if (shapeSelected) 6.dp else 16.dp,
        label = "animatedRadius"
    )
    val animatedWeight by animateFloatAsState(
        targetValue = if (shapeSelected) 0.25f else 0f
    )
    val iconSize = 70.dp
    IconButton(
        modifier = Modifier
            .padding(4.dp)
            .weight(itemWeight + animatedWeight)
            .height(iconSize)
            .pointerInput(Unit) {
                awaitPointerEventScope {
                    while (true) {
                        val event = awaitPointerEvent(PointerEventPass.Main)
                        event.changes.forEach { pointerInputChange ->
                            shapeSelected = pointerInputChange.pressed
                        }
                    }
                }
            },
        colors = IconButtonDefaults.filledIconButtonColors(
            containerColor = if (shapeSelected || checked) checkedColor else uncheckedColor,
            contentColor = if (shapeSelected || checked) uncheckedColor else checkedColor,
        ),
        shape = MaterialTheme.shapes.large.copy(CornerSize(animatedRadius)),
        onClick = onClick,
    ) {
        Icon(
            imageVector = icon,
            contentDescription = null,
        )
    }
}

Я изменил фиксированную ширину элементов с помощью весов, чтобы пользовательский интерфейс имел одинаковые пропорции на всех размерах экрана.

И вот окончательный результат:

Выразительные анимации для всех

Я решил написать все это сам, но вы можете использовать и настраивать это по своему усмотрению.

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

Подробнее о M3 Expressive вы можете узнать здесь.

Источник

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: