ข่าวผลิตภัณฑ์

มีอะไรใหม่ใน Jetpack Compose เวอร์ชันธันวาคม '25

ใช้เวลาอ่าน 6 นาที
Nick Butcher
ผู้จัดการผลิตภัณฑ์

วันนี้ Jetpack Compose เวอร์ชันธันวาคม '25 พร้อมให้ใช้งานอย่างเสถียรแล้ว ซึ่งประกอบด้วยโมดูลหลักของ Compose เวอร์ชัน 1.10 และ Material 3 เวอร์ชัน 1.4 (ดูการแมป BOM แบบเต็ม) พร้อมฟีเจอร์ใหม่และการปรับปรุงประสิทธิภาพที่สำคัญ

หากต้องการใช้เวอร์ชันที่เผยแพร่ในวันนี้ ให้อัปเกรดเวอร์ชัน Compose BOM เป็น 2025.12.00

implementation(platform("androidx.compose:compose-bom:2025.12.00"))

การปรับปรุงประสิทธิภาพ

เราทราบดีว่าประสิทธิภาพการทำงานของแอปในรันไทม์มีความสำคัญอย่างยิ่งต่อคุณและผู้ใช้ ทีม Compose จึงให้ความสำคัญกับการปรับปรุงประสิทธิภาพเป็นอันดับแรก เวอร์ชันนี้มีการปรับปรุงหลายอย่าง และคุณจะได้รับประโยชน์จากการปรับปรุงทั้งหมดนี้เพียงแค่อัปเกรดเป็นเวอร์ชันล่าสุด การทดสอบเปรียบเทียบการเลื่อนภายในแสดงให้เห็นว่าตอนนี้ Compose มีประสิทธิภาพเทียบเท่ากับการใช้ Views

janky.png

การทดสอบเปรียบเทียบประสิทธิภาพการเลื่อนระหว่าง Views กับ Jetpack Compose ใน Compose เวอร์ชันต่างๆ

การสร้างที่หยุดชั่วคราวได้ในการดึงข้อมูลล่วงหน้าแบบ Lazy

ตอนนี้ระบบจะเปิดใช้การสร้างที่หยุดชั่วคราวได้ในการดึงข้อมูลล่วงหน้าแบบ Lazy โดยค่าเริ่มต้น ซึ่งเป็นการเปลี่ยนแปลงพื้นฐานเกี่ยวกับวิธีที่รันไทม์ของ Compose กำหนดเวลาการทำงาน โดยออกแบบมาเพื่อลดการกระตุกอย่างมากในระหว่างเวิร์กโหลด UI ที่หนัก

ก่อนหน้านี้ เมื่อการสร้างเริ่มต้นขึ้นแล้ว การสร้างจะต้องทำงานให้เสร็จสมบูรณ์ หากการสร้างมีความซับซ้อน การสร้างอาจบล็อกเทรดหลักนานกว่า 1 เฟรม ทำให้ UI ค้าง แต่ตอนนี้การสร้างที่หยุดชั่วคราวได้ช่วยให้รันไทม์ "หยุดชั่วคราว" การทำงานได้หากเวลาใกล้หมด และกลับมาทำงานต่อในเฟรมถัดไป การดำเนินการนี้มีประสิทธิภาพอย่างยิ่งเมื่อใช้ร่วมกับการดึงข้อมูลล่วงหน้าของเลย์เอาต์แบบ Lazy เพื่อเตรียมเฟรมล่วงหน้า Lazy layout CacheWindow API ที่เปิดตัวใน Compose 1.9 เป็นวิธีที่ยอดเยี่ยมในการดึงข้อมูลล่วงหน้าของเนื้อหาเพิ่มเติมและใช้ประโยชน์จากการสร้างที่หยุดชั่วคราวได้เพื่อสร้างประสิทธิภาพ UI ที่ราบรื่นยิ่งขึ้น

pausable.gif

การสร้างที่หยุดชั่วคราวได้ร่วมกับการดึงข้อมูลล่วงหน้าแบบ Lazy ช่วยลดการกระตุกได้

นอกจากนี้ เรายังได้เพิ่มประสิทธิภาพในส่วนอื่นๆ ด้วยการปรับปรุง Modifier.onPlaced, Modifier.onVisibilityChanged และการติดตั้งใช้งานตัวปรับแต่งอื่นๆ เราจะยังคงลงทุนในการปรับปรุงประสิทธิภาพของ Compose ต่อไป

ฟีเจอร์ใหม่

Retain

Compose มี API หลายรายการสำหรับเก็บและจัดการสถานะในวงจรชีวิตต่างๆ เช่น remember จะเก็บสถานะไว้ในคอมโพสิชันต่างๆ และ rememberSavable/rememberSerializable จะเก็บสถานะไว้เมื่อมีการสร้างกิจกรรมหรือกระบวนการขึ้นใหม่retain เป็น API ใหม่ที่อยู่ระหว่าง API เหล่านี้ ซึ่งช่วยให้คุณเก็บค่าไว้ได้เมื่อมีการเปลี่ยนแปลงการกำหนดค่าโดยไม่ต้องทำให้เป็นอนุกรม แต่จะเก็บค่าไว้ไม่ได้เมื่อการประมวลผลสิ้นสุด เนื่องจาก retain ไม่ได้ทำให้สถานะของคุณเป็นอนุกรม คุณจึงเก็บออบเจ็กต์ต่างๆ เช่น นิพจน์แลมบ์ดา โฟลว์ และออบเจ็กต์ขนาดใหญ่ เช่น บิตแมป ซึ่งทำให้เป็นอนุกรมได้ยาก ตัวอย่างเช่น คุณอาจใช้ retain เพื่อจัดการเครื่องเล่นสื่อ (เช่น ExoPlayer) เพื่อให้การเล่นสื่อไม่ถูกขัดจังหวะเมื่อมีการเปลี่ยนแปลงการกำหนดค่า

@Composable

fun MediaPlayer() {

    val applicationContext = LocalContext.current.applicationContext

    val exoPlayer = retain { ExoPlayer.Builder(applicationContext).apply { ... }.build() }

    ...

}

เราขอขอบคุณชุมชน AndroidDev (โดยเฉพาะทีม Circuit) ที่มีส่วนร่วมและมีอิทธิพลต่อการออกแบบฟีเจอร์นี้

Material 1.4

ไลบรารี material3 เวอร์ชัน 1.4.0 มีคอมโพเนนต์และการปรับปรุงใหม่ๆ หลายรายการ ดังนี้

  • TextField ตอนนี้มีเวอร์ชันที่อิงตาม TextFieldState ซึ่งเป็นเวอร์ชันทดลองที่ให้วิธีที่แข็งแกร่งยิ่งขึ้นในการจัดการสถานะของข้อความ นอกจากนี้ ยังมี SecureTextField และ OutlinedSecureTextField เวอร์ชันใหม่ให้เลือกใช้แล้ว คอมโพสได้ Text ของ Material รองรับลักษณะการทำงาน autoSize แล้ว
  • ตอนนี้คอมโพเนนต์แคโรเซลมีเวอร์ชันใหม่ HorizontalCenteredHeroCarousel variant
  • TimePicker ตอนนี้รองรับการสลับระหว่างโหมดตัวเลือกและโหมดป้อนข้อมูลแล้ว
  • แฮนเดิลการลากแนวตั้งช่วยให้ผู้ใช้เปลี่ยนขนาดและ/หรือตำแหน่งของแผงแบบปรับได้
centered-hero-carousel.webp

แคโรเซลฮีโร่แบบกึ่งกลางแนวนอน

โปรดทราบว่าเราจะยังคงพัฒนา Material 3 Expressive API ในเวอร์ชันอัลฟ่าของไลบรารี material3 ต่อไป ดูข้อมูลเพิ่มเติมได้ในการพูดคุยล่าสุดนี้

ฟีเจอร์ภาพเคลื่อนไหวใหม่

เราจะขยาย API ภาพเคลื่อนไหวต่อไป ซึ่งรวมถึงการอัปเดตสำหรับการปรับแต่งภาพเคลื่อนไหวขององค์ประกอบแบบใช้ร่วมกัน

องค์ประกอบแบบใช้ร่วมกันแบบไดนามิก

โดยค่าเริ่มต้น ภาพเคลื่อนไหว sharedElement() และ sharedBounds() จะพยายามสร้างภาพเคลื่อนไหว

การเปลี่ยนแปลงเลย์เอาต์ทุกครั้งที่พบคีย์ที่ตรงกันในสถานะเป้าหมาย อย่างไรก็ตาม คุณอาจต้องการปิดใช้ภาพเคลื่อนไหวนี้แบบไดนามิกตามเงื่อนไขบางอย่าง เช่น ทิศทางการไปยังส่วนต่างๆ หรือสถานะ UI ปัจจุบัน

หากต้องการควบคุมว่าจะมีการเปลี่ยนภาพขององค์ประกอบแบบใช้ร่วมกันหรือไม่ ตอนนี้คุณสามารถปรับแต่ง SharedContentConfig ที่ส่งไปยัง rememberSharedContentState() ได้แล้ว พร็อพเพอร์ตี้ isEnabled จะกำหนดว่าองค์ประกอบแบบใช้ร่วมกันใช้งานได้หรือไม่

SharedTransitionLayout {

        val transition = updateTransition(currentState)

        transition.AnimatedContent { targetState ->

            // Create the configuration that depends on state changing.

            fun animationConfig() : SharedTransitionScope.SharedContentConfig {

                return object : SharedTransitionScope.SharedContentConfig {

                    override val SharedTransitionScope.SharedContentState.isEnabled: Boolean

                        get() =

                            // determine whether to perform a shared element transition

                }

            }

}

ดูข้อมูลเพิ่มเติมได้ในเอกสารประกอบ

Modifier.skipToLookaheadPosition()

เราได้เพิ่มตัวปรับแต่งใหม่ Modifier.skipToLookaheadPosition() ในเวอร์ชันนี้ ซึ่งจะคงตำแหน่งสุดท้ายของคอมโพสได้ไว้เมื่อสร้างภาพเคลื่อนไหวขององค์ประกอบแบบใช้ร่วมกัน การดำเนินการนี้ช่วยให้สร้างการเปลี่ยนภาพ เช่น ภาพเคลื่อนไหวประเภท "เปิดเผย" ได้ ดังที่เห็นในตัวอย่าง Androidify ที่มีการเปิดเผยกล้องแบบค่อยๆ ดูเคล็ดลับวิดีโอที่นี่เพื่อดูข้อมูลเพิ่มเติม

ความเร็วเริ่มต้นในการเปลี่ยนภาพขององค์ประกอบแบบใช้ร่วมกัน

เวอร์ชันนี้เพิ่ม API การเปลี่ยนภาพขององค์ประกอบแบบใช้ร่วมกันใหม่ prepareTransitionWithInitialVelocity ซึ่งช่วยให้คุณส่งความเร็วเริ่มต้น (เช่น จากท่าทางสัมผัส) ไปยังการเปลี่ยนภาพขององค์ประกอบแบบใช้ร่วมกันได้

Modifier.fillMaxSize()

    .draggable2D(

        rememberDraggable2DState { offset += it },

        onDragStopped = { velocity ->

            // Set up the initial velocity for the upcoming shared element

            // transition.

            sharedContentStateForDraggableCat

                ?.prepareTransitionWithInitialVelocity(velocity)

            showDetails = false

        },

    )
fling-shared.gif

การเปลี่ยนภาพขององค์ประกอบแบบใช้ร่วมกันที่เริ่มต้นด้วยความเร็วเริ่มต้นจากท่าทางสัมผัส

การเปลี่ยนภาพแบบมีม่าน

EnterTransition และ ExitTransition จะกำหนดลักษณะที่คอมโพสได้ AnimatedVisibility/AnimatedContent ปรากฏหรือหายไป ตัวเลือกม่านแบบทดลองใหม่ช่วยให้คุณระบุสีเพื่อใช้เป็นม่านหรือฉากกั้นเนื้อหาได้ เช่น การค่อยๆ แสดง/ซ่อนเลเยอร์สีดำแบบกึ่งโปร่งแสงเหนือเนื้อหา

veil_2.gif

เนื้อหาแบบเคลื่อนไหวที่มีม่าน โปรดสังเกตม่าน (หรือฉากกั้น) แบบกึ่งโปร่งแสงเหนือเนื้อหาตารางกริดระหว่างภาพเคลื่อนไหว

AnimatedContent(

    targetState = page,

    modifier = Modifier.fillMaxSize().weight(1f),

    transitionSpec = {

        if (targetState > initialState) {

            (slideInHorizontally { it } togetherWith

                    slideOutHorizontally { -it / 2 } + veilOut(targetColor = veilColor))

        } else {

            slideInHorizontally { -it / 2 } +

                    unveilIn(initialColor = veilColor) togetherWith slideOutHorizontally { it }

        }

    },

) { targetPage ->

    ...

}

การเปลี่ยนแปลงที่กำลังจะเกิดขึ้น

การเลิกใช้งาน Modifier.onFirstVisible

Compose 1.9 ได้เปิดตัว Modifier.onVisibilityChanged และ Modifier.onFirstVisible หลังจากตรวจสอบความคิดเห็นของคุณแล้ว เราพบว่าสัญญาของ Modifier.onFirstVisible ไม่สามารถดำเนินการได้อย่างแน่นอน โดยเฉพาะอย่างยิ่งเมื่อรายการปรากฏ เป็นครั้งแรก ตัวอย่างเช่น เลย์เอาต์แบบ Lazy อาจทิ้งรายการที่เลื่อนออกนอกวิวพอร์ต แล้วสร้างรายการเหล่านั้นขึ้นมาอีกครั้งหากมีการเลื่อนกลับเข้ามาในมุมมอง ในกรณีนี้ ระบบจะเรียกใช้การเรียกกลับ onFirstVisible อีกครั้ง เนื่องจากเป็นรายการที่สร้างขึ้นใหม่ ลักษณะการทำงานที่คล้ายกันนี้จะเกิดขึ้นเมื่อกลับไปที่หน้าจอที่เคยเข้าชมซึ่งมี onFirstVisible ดังนั้น เราจึงตัดสินใจเลิกใช้งานตัวปรับแต่งนี้ใน Compose เวอร์ชันถัดไป (1.11) และแนะนำให้ย้ายข้อมูลไปใช้ onVisibilityChanged ดูข้อมูลเพิ่มเติมได้ในเอกสารประกอบ

การส่งโครูทีนในการทดสอบ

เราวางแผนที่จะเปลี่ยนการส่งโครูทีนในการทดสอบเพื่อลดความไม่แน่นอนของการทดสอบและตรวจพบปัญหาเพิ่มเติม ปัจจุบันการทดสอบใช้ UnconfinedTestDispatcher ซึ่งแตกต่างจากลักษณะการทำงานในเวอร์ชันที่ใช้งานจริง เช่น เอฟเฟกต์อาจทำงานทันทีแทนที่จะเข้าคิว ในเวอร์ชันที่จะเผยแพร่ในอนาคต เราวางแผนที่จะเปิดตัว API ใหม่ที่ใช้ StandardTestDispatcher โดยค่าเริ่มต้นเพื่อให้ตรงกับลักษณะการทำงานในเวอร์ชันที่ใช้งานจริง คุณลองใช้ลักษณะการทำงานใหม่ได้แล้ว ในเวอร์ชัน 1.10 โดยทำดังนี้

@get:Rule // also createAndroidComposeRule, createEmptyComposeRule

val rule = createComposeRule(effectContext = StandardTestDispatcher())

การใช้ StandardTestDispatcher จะเข้าคิวงาน ดังนั้นคุณต้องใช้กลไกการซิงโครไนซ์ เช่น composeTestRule.waitForIdle() หรือ composeTestRule.runOnIdle() หากการทดสอบใช้ runTest คุณต้องตรวจสอบว่า runTest และกฎ Compose ของคุณใช้อินสแตนซ์ StandardTestDispatcher เดียวกันสำหรับการซิงโครไนซ์

// 1. Create a SINGLE dispatcher instance

val testDispatcher = StandardTestDispatcher()



// 2. Pass it to your Compose rule

@get:Rule

val composeRule = createComposeRule(effectContext = testDispatcher)



@Test

// 3. Pass the *SAME INSTANCE* to runTest

fun myTest() = runTest(testDispatcher) {

    composeRule.setContent { /* ... */ }

}

เครื่องมือ

API ที่ยอดเยี่ยมควรมีเครื่องมือที่ยอดเยี่ยม และ Android Studio ก็มีเครื่องมือใหม่ๆ มากมายสำหรับนักพัฒนาแอป Compose ดังนี้

หากต้องการดูเครื่องมือเหล่านี้ในการทำงาน โปรดดูการสาธิตล่าสุดนี้

ขอให้สนุกกับการเขียนโค้ด

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

เขียนโดย

อ่านต่อ