Irbisa · Шпаргалка
Анимации и жесты в Compose
animate*AsState, Transition, AnimatedContent, Animatable, pointerInput, drag, nestedScroll, Canvas
Как анимировать изменение размера composable, когда его содержимое растёт, например у раскрывающейся карточки?
@Composablefun ExpandableCard(title: String, details: String) { var expanded by remember { mutableStateOf(false) } Card(onClick = { expanded = !expanded }) { Column( Modifier…Как анимировать появление и исчезновение composable и как настроить анимацию входа и выхода?
@Composablefun Details(details: String) { var visible by remember { mutableStateOf(false) } Column { Button(onClick = { visible = !visible }) { Text("Toggle") } AnimatedVisibility(…Как сделать бесконечно повторяющуюся анимацию, например пульсирующую точку или шиммер загрузки?
@Composablefun PulsingDot(modifier: Modifier = Modifier) { val transition = rememberInfiniteTransition(label = "pulse") val scale by transition.animateFloat( initialValue = 0.8f, targetValue = 1.2f,…Счётчик ниже анимируется, но и уходящий, и появляющийся текст показывают новое число. Что не так?
@Composablefun Total(count: Int) { AnimatedContent( targetState = count, transitionSpec = { slideInVertically { it } togetherWith slideOutVertically { -it } }, label = "total", ) { Text("Total: $count") }}Когда для обработки нажатий использовать
clickable,combinedClickableилиpointerInput { detectTapGestures }?// a post: tap opens, double tap likes, long press shows the menuBox( Modifier .clip(MaterialTheme.shapes.medium) // clip first, so the ripple is rounded .combinedClickable( onClick = onOpen,…Как рисовать произвольную графику в Compose и чем отличаются
Canvas,drawBehindиdrawWithContent?@Composablefun ProgressRing(progress: () -> Float, modifier: Modifier = Modifier) { val track = MaterialTheme.colorScheme.surfaceVariant val fill = MaterialTheme.colorScheme.primary Canvas(modifier.size(64.dp)) { // progress() is read inside the draw lambda: redraw only val stroke = Stroke(width = 6.dp.toPx(), cap = StrokeCap.Round)…Чем отличаются
animate*AsState,updateTransitionиAnimatableи когда что выбирать?Версии A и B анимируют одни и те же два
Animatable. Сколько длится каждая и в каком порядке меняются значения?val alpha = remember { Animatable(0f) }val offsetY = remember { Animatable(40f) } // version ALaunchedEffect(Unit) { alpha.animateTo(1f, tween(300)) offsetY.animateTo(0f, tween(300))} // version BLaunchedEffect(Unit) { launch { alpha.animateTo(1f, tween(300)) } launch { offsetY.animateTo(0f, tween(300)) }}Найдите баг: при каждом изменении
progressэта полоска падает до нуля и заполняется заново, вместо того чтобы продолжить с текущей ширины.@Composablefun ProgressBar(progress: Float, modifier: Modifier = Modifier) { val animated = Animatable(0f) LaunchedEffect(progress) { animated.animateTo(progress, animationSpec = tween(500)) } Box( modifier .fillMaxWidth() .height(8.dp) .drawBehind { drawRect(Color(0xFF3D7BFF), size = size.copy(width = size.width * animated.value)) } )}Когда выбирать
spring,tweenилиkeyframesкак спецификацию анимации и зачем нуженvisibilityThreshold?// exact curve from the design specval alpha by animateFloatAsState( targetValue = if (visible) 1f else 0f, animationSpec = tween(300, easing = CubicBezierEasing(0.2f, 0f, 0f, 1f)), label = "alpha",)…Как анимировать добавление, удаление и перестановку элементов в
LazyColumn?@Composablefun TaskList(tasks: List<Task>, onToggle: (String) -> Unit) { LazyColumn { items(tasks, key = { it.id }) { task -> // stable, unique key TaskRow( task = task,…Как работают shared element transitions в Compose и как подключить их к Navigation Compose?
@Serializable object Covers@Serializable data class Detail(val id: String) @OptIn(ExperimentalSharedTransitionApi::class)@Composablefun App(nav: NavHostController) {…Как Compose учитывает системную настройку «Удалить анимации» и что всё равно нужно обработать самому?
Что делает
Modifier.graphicsLayerи почему через него лучше всего анимировать прозрачность, масштаб и поворот?@Composablefun FlipCard(flipped: Boolean, front: @Composable () -> Unit, back: @Composable () -> Unit) { val rotation by animateFloatAsState(if (flipped) 180f else 0f, label = "flip") val showBack by remember { derivedStateOf { rotation > 90f } } Box( Modifier.graphicsLayer { // rotation is read here, in the draw phase…Как написать собственный жест через
Modifier.pointerInputи за что отвечают его ключи?@Composablefun VolumeStrip(onChange: (Float) -> Unit, modifier: Modifier = Modifier) { val change by rememberUpdatedState(onChange) // always the latest callback Box( modifier .size(48.dp, 200.dp)…Найдите баг: при перетаскивании этот чип убегает вперёд пальца, и разрыв растёт тем больше, чем дальше тянуть.
@Composablefun DraggableChip(label: String) { var offsetX by remember { mutableFloatStateOf(0f) } Text( text = label, modifier = Modifier .offset(x = offsetX.dp) .pointerInput(Unit) { detectHorizontalDragGestures { change, dragAmount -> change.consume() offsetX += dragAmount } } )}Ручка перетаскивания на
detectDragGesturesвнутри строкиLazyColumnблокирует прокрутку списка. Как Compose решает, какой жест побеждает?Как реализовать pinch-to-zoom и перемещение картинки в Compose?
@Composablefun ZoomableImage(painter: Painter, modifier: Modifier = Modifier) { var scale by remember { mutableFloatStateOf(1f) } var offset by remember { mutableStateOf(Offset.Zero) } val state = rememberTransformableState { zoomChange, panChange, _ -> scale = (scale * zoomChange).coerceIn(1f, 5f)…Как сделать строку со свайпом для удаления или любой перетаскиваемый элемент, который защёлкивается в фиксированных позициях?
enum class Swipe { Settled, Dismissed }@Composablefun SwipeToDeleteRow(item: Item, onDelete: () -> Unit) { val state = remember { AnchoredDraggableState(initialValue = Swipe.Settled) } LaunchedEffect(state.settledValue) { // act when the animation lands if (state.settledValue == Swipe.Dismissed) onDelete()…Ваша отрисовка создаёт
PathиBrushна каждом кадре внутриdrawBehind. Что меняетdrawWithCache?@Composablefun Gauge(progress: Float, modifier: Modifier = Modifier) { val sweep by animateFloatAsState(progress * 270f, label = "sweep") Box( modifier .size(160.dp)…Как сделать, чтобы перетаскиваемый элемент после отпускания продолжал двигаться со скоростью пальца, и что для этого нужно
Animatableво время и после жеста?@Composablefun FlingCard(modifier: Modifier = Modifier) { val offsetX = remember { Animatable(0f) } val scope = rememberCoroutineScope() val decay = rememberSplineBasedDecay<Float>()…Как работает вложенная прокрутка (nested scroll) в Compose и как сделать сворачивающийся заголовок через
NestedScrollConnection?class CollapsingHeader(private val maxPx: Float) : NestedScrollConnection { var offset by mutableFloatStateOf(0f) // -maxPx..0f private set // finger up: the header shrinks BEFORE the list scrolls override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { if (available.y >= 0f) return Offset.Zero…Как анимировать изменение раскладки, при котором элементы переезжают на новые позиции, например переключение списка в сетку из двух колонок?
@OptIn(ExperimentalSharedTransitionApi::class, ExperimentalLayoutApi::class)@Composablefun Gallery(items: List<Item>, grid: Boolean) { LookaheadScope { FlowRow( Modifier.animateContentSize(), // the container size changes too…Как размыть composable или применить собственный шейдер и что показывать на версиях Android, где это недоступно?
@Composablefun CoverImage(modifier: Modifier = Modifier) { // fallback decided in one place if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // API 31+ Box(modifier.blur(16.dp, BlurredEdgeTreatment.Unbounded)) { Cover() } } else {…