اصلاح‌کننده‌های پویانمایی و عناصر ترکیبی

Compose با تابع‌های ترکیب‌پذیر و اصلاح‌گرهای داخلی برای مدیریت موارد استفاده رایج پویانمایی ارائه می‌شود.

ترکیب‌شدنی‌های پویانمایی‌شده داخلی

‫Compose چندین ترکیب‌کردنی ارائه می‌دهد که ظاهر محتوا، ناپدید شدن، و تغییرات چیدمان را پویانمایی می‌کنند.

پویانمایی ظاهر شدن و ناپدید شدن

ترکیب‌پذیر سبز که خودش را نشان می‌دهد و پنهان می‌کند
شکل ۱. پویانمایی ظاهر شدن و ناپدید شدن موردی در ستون.

ترکیب‌پذیر AnimatedVisibility ظاهر شدن و ناپدید شدن محتوای خود را پویانمایی می‌کند.

var visible by remember {
    mutableStateOf(true)
}
// Animated visibility will eventually remove the item from the composition once the animation has finished.
AnimatedVisibility(visible) {
    // your composable here
    // ...
}

به‌طور پیش‌فرض، محتوا با محو شدن و گسترش ظاهر می‌شود و با محو شدن و کوچک شدن ناپدید می‌شود. با مشخص کردن اشیای EnterTransition و ExitTransition، این انتقال را سفارشی‌سازی کنید.

var visible by remember { mutableStateOf(true) }
val density = LocalDensity.current
AnimatedVisibility(
    visible = visible,
    enter = slideInVertically {
        // Slide in from 40 dp from the top.
        with(density) { -40.dp.roundToPx() }
    } + expandVertically(
        // Expand from the top.
        expandFrom = Alignment.Top
    ) + fadeIn(
        // Fade in with the initial alpha of 0.3f.
        initialAlpha = 0.3f
    ),
    exit = slideOutVertically() + shrinkVertically() + fadeOut()
) {
    Text(
        "Hello",
        Modifier
            .fillMaxWidth()
            .height(200.dp)
    )
}

همان‌طور که در مثال قبلی نشان داده شده است، می‌توانید چند شیء EnterTransition یا ExitTransition را با یک عملگر + ترکیب کنید و هرکدام پارامترهای اختیاری را برای سفارشی‌سازی رفتار خود می‌پذیرند. برای اطلاعات بیشتر، به صفحه‌های مرجع مراجعه کنید.

نمونه‌های گذار ورود و خروج

EnterTransition ExitTransition
fadeIn
عنصر میانای کاربر به‌تدریج در نما محو می‌شود.
fadeOut
عنصر میانای کاربر به‌تدریج از دید محو می‌شود.
slideIn
عنصر میانای کاربر از خارج از صفحه به داخل صفحه می‌لغزد.
slideOut
عنصر میانای کاربر از دید خارج می‌شود و از صفحه بیرون می‌رود.
slideInHorizontally
عنصر میانای کاربر به‌صورت افقی وارد صحنه می‌شود.
slideOutHorizontally
عنصر میانای کاربر به‌صورت افقی از دید خارج می‌شود.
slideInVertically
عنصر میانای کاربر به‌صورت عمودی در نما می‌لغزد.
slideOutVertically
عنصر میانای کاربر به‌صورت عمودی از دید خارج می‌شود.
scaleIn
عنصر میانای کاربر بزرگ می‌شود و در نما قرار می‌گیرد.
scaleOut
یک عنصر میانای کاربر کوچک می‌شود و از دید خارج می‌شود.
expandIn
عنصر میانای کاربر از نقطه‌ای مرکزی ازهم باز می‌شود و در نما قرار می‌گیرد.
shrinkOut
یک عنصر میانای کاربر از دید خارج می‌شود و به یک نقطه مرکزی تبدیل می‌شود.
expandHorizontally
عنصر میانای کاربر به‌صورت افقی ازهم باز می‌شود و در نما قرار می‌گیرد.
shrinkHorizontally
عنصر میانای کاربر به‌صورت افقی کوچک می‌شود و از دید خارج می‌شود.
expandVertically
عنصر میانای کاربر به‌صورت عمودی در نما ازهم باز می‌شود.
shrinkVertically
عنصر میانای کاربر به‌صورت عمودی کوچک می‌شود و از دید خارج می‌شود.

‫AnimatedVisibility همچنین متغیری را ارائه می‌دهد که آرگومان MutableTransitionState را می‌گیرد. این به شما امکان می‌دهد به‌محض اینکه AnimatedVisibility composable به درخت ترکیب‌بندی اضافه شد، پویانمایی را راه‌اندازی کنید. همچنین برای مشاهده وضعیت پویانمایی مفید است.

// Create a MutableTransitionState<Boolean> for the AnimatedVisibility.
val state = remember {
    MutableTransitionState(false).apply {
        // Start the animation immediately.
        targetState = true
    }
}
Column {
    AnimatedVisibility(visibleState = state) {
        Text(text = "Hello, world!")
    }

    // Use the MutableTransitionState to know the current animation state
    // of the AnimatedVisibility.
    Text(
        text = when {
            state.isIdle && state.currentState -> "Visible"
            !state.isIdle && state.currentState -> "Disappearing"
            state.isIdle && !state.currentState -> "Invisible"
            else -> "Appearing"
        }
    )
}

پویانمایی کردن ورود و خروج برای کودکان

محتوای درون AnimatedVisibility (فرزندان مستقیم یا غیرمستقیم) می‌تواند از اصلاح‌کننده animateEnterExit برای مشخص کردن رفتار پویانمایی متفاوت برای هرکدام از آن‌ها استفاده کند. جلوه بصری برای هریک از این فرزندان ترکیبی از پویانمایی‌های مشخص‌شده در AnimatedVisibility composable و پویانمایی‌های ورود و خروج خود فرزند است.

var visible by remember { mutableStateOf(true) }

AnimatedVisibility(
    visible = visible,
    enter = fadeIn(),
    exit = fadeOut()
) {
    // Fade in/out the background and the foreground.
    Box(
        Modifier
            .fillMaxSize()
            .background(Color.DarkGray)
    ) {
        Box(
            Modifier
                .align(Alignment.Center)
                .animateEnterExit(
                    // Slide in/out the inner box.
                    enter = slideInVertically(),
                    exit = slideOutVertically()
                )
                .sizeIn(minWidth = 256.dp, minHeight = 64.dp)
                .background(Color.Red)
        ) {
            // Content of the notification…
        }
    }
}

در برخی موارد، ممکن است بخواهید AnimatedVisibility هیچ پویانمایی‌ای اعمال نکند تا کودکان بتوانند هرکدام پویانمایی متمایز خودشان را با animateEnterExit داشته باشند. برای دستیابی به این هدف، EnterTransition.None و ExitTransition.None را در AnimatedVisibility composable مشخص کنید.

افزودن پویانمایی سفارشی

اگر می‌خواهید جلوه‌های پویانمایی سفارشی فراتر از پویانمایی‌های ورود و خروج داخلی اضافه کنید، بااستفاده از ویژگی transition در لامبدای محتوا برای AnimatedVisibility، به نمونه Transition زیرین دسترسی پیدا کنید. هر حالت پویانمایی که به نمونه «گذار» اضافه شود، هم‌زمان با پویانمایی‌های ورود و خروج AnimatedVisibility اجرا خواهد شد. AnimatedVisibility تا زمانی که همه پویانمایی‌ها در Transition تمام شوند صبر می‌کند و سپس محتوای آن را برمی‌دارد. برای پویانمایی‌های خروج که مستقل از Transition ایجاد شده‌اند (مثلاً بااستفاده از animate*AsState)، AnimatedVisibility نمی‌تواند آن‌ها را درنظر بگیرد، و بنابراین ممکن است محتوای قابل‌ترکیب را قبل‌از اتمام آن‌ها بردارد.

var visible by remember { mutableStateOf(true) }

AnimatedVisibility(
    visible = visible,
    enter = fadeIn(),
    exit = fadeOut()
) { // this: AnimatedVisibilityScope
    // Use AnimatedVisibilityScope#transition to add a custom animation
    // to the AnimatedVisibility.
    val background by transition.animateColor(label = "color") { state ->
        if (state == EnterExitState.Visible) Color.Blue else Color.Gray
    }
    Box(
        modifier = Modifier
            .size(128.dp)
            .background(background)
    )
}

برای کسب اطلاعات بیشتر درباره استفاده از Transition برای مدیریت پویانمایی‌ها، به پویانمایی هم‌زمان چند ویژگی با انتقال مراجعه کنید.

پویانمایی براساس وضعیت هدف

AnimatedContent ترکیب‌پذیر محتوای خود را هنگام تغییر براساس حالت هدف پویانمایی می‌کند.

Row {
    var count by remember { mutableIntStateOf(0) }
    Button(onClick = { count++ }) {
        Text("Add")
    }
    AnimatedContent(
        targetState = count,
        label = "animated content"
    ) { targetCount ->
        // Make sure to use `targetCount`, not `count`.
        Text(text = "Count: $targetCount")
    }
}

به‌طور پیش‌فرض، محتوای اولیه محو می‌شود و سپس محتوای هدف ظاهر می‌شود (به این رفتار محو شدن می‌گویند). می‌توانید این رفتار پویانمایی را با مشخص کردن یک شیء ContentTransform برای پارامتر transitionSpec سفارشی‌سازی کنید. می‌توانید با ترکیب کردن شیء EnterTransition با شیء ExitTransition بااستفاده از تابع میان‌وند with، نمونه‌ای از ContentTransform ایجاد کنید. می‌توانید SizeTransform را با پیوست کردن آن با تابع میان‌وند using به شیء ContentTransform اعمال کنید.

AnimatedContent(
    targetState = count,
    transitionSpec = {
        // Compare the incoming number with the previous number.
        if (targetState > initialState) {
            // If the target number is larger, it slides up and fades in
            // while the initial (smaller) number slides up and fades out.
            slideInVertically { height -> height } + fadeIn() togetherWith
                slideOutVertically { height -> -height } + fadeOut()
        } else {
            // If the target number is smaller, it slides down and fades in
            // while the initial number slides down and fades out.
            slideInVertically { height -> -height } + fadeIn() togetherWith
                slideOutVertically { height -> height } + fadeOut()
        }.using(
            // Disable clipping since the faded slide-in/out should
            // be displayed out of bounds.
            SizeTransform(clip = false)
        )
    }, label = "animated content"
) { targetCount ->
    Text(text = "$targetCount")
}

EnterTransition نحوه ظاهر شدن محتوای هدف را تعریف می‌کند، و ExitTransition نحوه ناپدید شدن محتوای اولیه را تعریف می‌کند. علاوه‌بر همه عملکردهای EnterTransition و ExitTransition که برای AnimatedVisibility دردسترس است، AnimatedContent slideIntoContainer و slideOutOfContainer را ارائه می‌دهد. این‌ها جایگزین‌های مناسبی برای slideInHorizontally/Vertically و slideOutHorizontally/Vertically هستند که فاصله لغزش را براساس اندازه محتوای اولیه و محتوای هدف محتوای AnimatedContent محاسبه می‌کنند.

SizeTransform نحوه پویانمایی اندازه بین محتوای اولیه و هدف را تعریف می‌کند. هنگام ایجاد پویانمایی، به اندازه اولیه و اندازه هدف دسترسی دارید. SizeTransform همچنین کنترل می‌کند که آیا محتوا باید درطول پویانمایی‌ها به اندازه مؤلفه برش داده شود یا نه.

var expanded by remember { mutableStateOf(false) }
Surface(
    color = MaterialTheme.colorScheme.primary,
    onClick = { expanded = !expanded }
) {
    AnimatedContent(
        targetState = expanded,
        transitionSpec = {
            fadeIn(animationSpec = tween(150, 150)) togetherWith
                fadeOut(animationSpec = tween(150)) using
                SizeTransform { initialSize, targetSize ->
                    if (targetState) {
                        keyframes {
                            // Expand horizontally first.
                            IntSize(targetSize.width, initialSize.height) at 150
                            durationMillis = 300
                        }
                    } else {
                        keyframes {
                            // Shrink vertically first.
                            IntSize(initialSize.width, targetSize.height) at 150
                            durationMillis = 300
                        }
                    }
                }
        }, label = "size transform"
    ) { targetExpanded ->
        if (targetExpanded) {
            Expanded()
        } else {
            ContentIcon()
        }
    }
}

پویانمایی کردن انتقال‌های ورود و خروج فرزند

درست مثل AnimatedVisibility، اصلاح‌کننده animateEnterExit در داخل لامبدای محتوای AnimatedContent دردسترس است. از این گزینه برای اعمال EnterAnimation و ExitAnimation به‌طور جداگانه بر هریک از فرزندان مستقیم یا غیرمستقیم استفاده کنید.

افزودن پویانمایی سفارشی

درست مثل AnimatedVisibility، فیلد transition در داخل محتوای لامبدا AnimatedContent دردسترس است. از این برای ایجاد جلوه پویانمایی سفارشی استفاده کنید که هم‌زمان با گذر AnimatedContent اجرا می‌شود. برای جزئیات، updateTransition را ببینید.

پویانمایی بین دو چیدمان

‫Crossfade بین دو چیدمان با پویانمایی محو تدریجی متقاطع پویانمایی می‌کند. با تغییر دادن مقدار ارسال‌شده به پارامتر current، محتوا با پویانمایی محو تدریجی متقاطع عوض می‌شود.

var currentPage by remember { mutableStateOf("A") }
Crossfade(targetState = currentPage, label = "cross fade") { screen ->
    when (screen) {
        "A" -> Text("Page A")
        "B" -> Text("Page B")
    }
}

اصلاح‌کننده‌های پویانمایی داخلی

Compose اصلاح‌کننده‌هایی برای پویانمایی تغییرات خاص مستقیماً روی composable ارائه می‌دهد.

پویانمایی تغییرات اندازه ترکیب‌پذیر

ترکیب‌پذیر سبز که تغییر اندازه خود را به‌صورت روان پویانمایی می‌کند.
شکل ۲. Composable smoothly animating between a small and a larger size

تغییردهنده animateContentSize تغییر اندازه را پویانمایی می‌کند.

var expanded by remember { mutableStateOf(false) }
Box(
    modifier = Modifier
        .background(colorBlue)
        .animateContentSize()
        .height(if (expanded) 400.dp else 200.dp)
        .fillMaxWidth()
        .clickable(
            interactionSource = remember { MutableInteractionSource() },
            indication = null
        ) {
            expanded = !expanded
        }

) {
}

پویانمایی‌های مورد فهرست

اگر می‌خواهید ترتیب مجدد موارد را در یک فهرست یا شبکه Lazy پویانمایی کنید، سند پویانمایی مورد طرح‌بندی Lazy را ببینید.