ตัวแก้ไขภาพเคลื่อนไหวและ Composable

Compose มาพร้อมกับ Composable และตัวแก้ไขในตัวสำหรับจัดการกรณีการใช้งานภาพเคลื่อนไหวทั่วไป

Composable แบบเคลื่อนไหวในตัว

สร้างภาพเคลื่อนไหวจากการปรากฏและการหายไปด้วย AnimatedVisibility

วันที่ Composable สีเขียวแสดงและซ่อนตัวเอง
รูปที่ 1 การสร้างภาพเคลื่อนไหวให้กับลักษณะที่ปรากฏและการหายไปของรายการในคอลัมน์

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 จะถูกเพิ่มลงในแผนผังการเรียบเรียง และยังมีประโยชน์สำหรับ โดยสังเกตสถานะภาพเคลื่อนไหว

// 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 และการป้อน Enter และของผู้เผยแพร่โฆษณาย่อยเอง ออกจากภาพเคลื่อนไหว

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 ที่สำคัญผ่าน transition ภายในเนื้อหา lambda สำหรับ AnimatedVisibility ภาพเคลื่อนไหวแบบใดก็ได้ สถานะที่เพิ่มลงในอินสแตนซ์การเปลี่ยนจะทำงานพร้อมกันกับ และภาพเคลื่อนไหวออกจาก AnimatedVisibility AnimatedVisibility รอจนถึง ภาพเคลื่อนไหวทั้งหมดใน Transition จบแล้วก่อนที่จะนำเนื้อหาออก สำหรับภาพเคลื่อนไหวการออกที่สร้างขึ้นโดยไม่ขึ้นกับ Transition (เช่น การใช้ animate*AsState) AnimatedVisibility จะใช้บัญชีของตนเองไม่ได้ ดังนั้นจึงอาจนำเนื้อหาที่เขียนได้ด้วย Compose ได้ออกก่อนที่จะสิ้นสุด

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

AnimatedContent Composable ทำให้เนื้อหาเคลื่อนไหวเมื่อเปลี่ยนไปตาม สถานะเป้าหมาย

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

โปรดทราบว่าคุณควรใช้พารามิเตอร์ lambda เสมอและแสดงพารามิเตอร์ lambda เนื้อหา API จะใช้ค่านี้เป็นคีย์ในการระบุเนื้อหาที่ แสดงอยู่ในปัจจุบัน

โดยค่าเริ่มต้น เนื้อหาเริ่มต้นจะค่อยๆ เลือนหายไป จากนั้นเนื้อหาเป้าหมายจะค่อยๆ ปรากฏขึ้น (ลักษณะการทำงานนี้เรียกว่า fade through) คุณ สามารถปรับแต่งลักษณะการทำงานของภาพเคลื่อนไหวนี้ได้โดยการระบุออบเจ็กต์ ContentTransform ไปยัง พารามิเตอร์ transitionSpec คุณสามารถสร้าง ContentTransform โดยรวม EnterTransition กับ ExitTransition โดยใช้ฟังก์ชัน with Infix คุณสามารถใช้ SizeTransform ลงใน ContentTransform โดยแนบไว้กับไฟล์ ฟังก์ชัน using Infix

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() with
                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() with
                slideOutVertically { height -> height } + fadeOut()
        }.using(
            // Disable clipping since the faded slide-in/out should
            // be displayed out of bounds.
            SizeTransform(clip = false)
        )
    }
) { 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)) with
                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
                        }
                    }
                }
        }
    ) { targetExpanded ->
        if (targetExpanded) {
            Expanded()
        } else {
            ContentIcon()
        }
    }
}

สร้างภาพเคลื่อนไหวการเปลี่ยนให้เข้าและออกของบุตรหลาน

เช่นเดียวกับ AnimatedVisibility animateEnterExit มีตัวแก้ไขอยู่ภายในเนื้อหาของ AnimatedContent ได้ ใช้ร่างคำตอบนี้ เพื่อใช้ EnterAnimation และ ExitAnimation กับทั้งโดยตรงและโดยอ้อมแต่ละรายการ ผู้เผยแพร่โฆษณาย่อยๆ แยกกัน

เพิ่มภาพเคลื่อนไหวที่กำหนดเอง

เช่นเดียวกับ AnimatedVisibility ช่อง transition จะอยู่ภายใน เนื้อหา lambda ของ AnimatedContent ใช้ตัวเลือกนี้เพื่อสร้างภาพเคลื่อนไหวที่กำหนดเอง ที่ทำงานพร้อมกันกับการเปลี่ยน AnimatedContent โปรดดู updateTransition เพื่อดูรายละเอียด

แสดงภาพเคลื่อนไหวระหว่าง 2 เลย์เอาต์ด้วย Crossfade

Crossfade ทำภาพเคลื่อนไหวระหว่าง 2 เลย์เอาต์ด้วยภาพเคลื่อนไหวแบบครอสเฟด สลับ ค่าที่ส่งไปยังพารามิเตอร์ current เนื้อหาจะถูกสลับด้วย ครอสเฟดด้วยภาพเคลื่อนไหว

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

ตัวแก้ไขภาพเคลื่อนไหวในตัว

สร้างภาพเคลื่อนไหวการเปลี่ยนขนาดที่ประกอบได้ด้วย animateContentSize

วันที่ ภาพเคลื่อนไหวที่ประกอบกันได้สีเขียวทำให้การเปลี่ยนขนาดดำเนินไปอย่างลื่นไหล
รูปที่ 2 สามารถประกอบกันได้ได้อย่างราบรื่นซึ่งสร้างภาพเคลื่อนไหวระหว่างขนาดเล็กกับขนาดใหญ่

ตัวแก้ไข 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 Loading