การวางเลเยอร์สถาปัตยกรรมของ Jetpack Compose

หน้าเว็บนี้นำเสนอภาพรวมระดับสูงของเลเยอร์ทางสถาปัตยกรรมที่ Jetpack Compose และหลักการสำคัญที่ใช้ประกอบการออกแบบนี้

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

  • ใช้ระดับ Abstraction ที่เหมาะสมในการสร้างแอปหรือคลัง
  • ทำความเข้าใจว่าเมื่อใดที่คุณสามารถ "เลื่อนลง" ไปยังระดับที่ต่ำลงเพื่อให้ควบคุมได้มากขึ้น หรือ การปรับแต่ง
  • ลดทรัพยากร Dependency ให้น้อยที่สุด

เลเยอร์

เลเยอร์หลักของ Jetpack Compose ได้แก่

รูปที่ 1 เลเยอร์หลักของ Jetpack Compose

แต่ละเลเยอร์จะสร้างขึ้นจากระดับที่ต่ำกว่า เป็นการรวมฟังก์ชันต่างๆ ในการสร้าง ระดับสูงขึ้น แต่ละเลเยอร์สร้างขึ้นบน API สาธารณะของเลเยอร์ที่ต่ำกว่า เพื่อตรวจสอบขอบเขตของโมดูล และทำให้สามารถแทนที่เลเยอร์ใดๆ ได้ จำเป็น มาดูเลเยอร์เหล่านี้จากด้านล่างขึ้นบนกัน

รันไทม์
โมดูลนี้จะให้ข้อมูลพื้นฐานเกี่ยวกับรันไทม์ของ Compose เช่น remember, mutableStateOf, เวลา @Composable คำอธิบายประกอบและ SideEffect คุณอาจลองสร้างบนเลเยอร์นี้โดยตรงหากคุณต้องการ ความสามารถในการจัดการแผนผังของ Compose ไม่ใช่ UI
UI
เลเยอร์ UI ประกอบด้วยโมดูลหลายรายการ ( ui-text, ui-graphics, ui-tooling เป็นต้น) โมดูลเหล่านี้จะนำพื้นฐานของชุดเครื่องมือ UI ไปใช้ เช่น LayoutNode, Modifier, เครื่องจัดการอินพุต การจัดวางที่กำหนดเอง และการวาดภาพ คุณอาจพิจารณาสร้างเลเยอร์นี้หาก คุณต้องมีแค่แนวคิดพื้นฐานของชุดเครื่องมือ UI เท่านั้น
การก่อตั้ง
โมดูลนี้มีองค์ประกอบที่ใช้สร้างสรรค์เกี่ยวกับระบบการออกแบบสำหรับ Compose UI ชอบ Row และ Column, LazyColumn การรู้จำท่าทางบางอย่าง ฯลฯ คุณอาจพิจารณาสร้าง พื้นฐานเพื่อสร้างระบบออกแบบของคุณเอง
วัสดุ
โมดูลนี้นำเสนอการใช้ระบบดีไซน์ Material สำหรับ UI ของ Compose, ระบบการกำหนดธีม คอมโพเนนต์ที่มีการจัดรูปแบบ และ Ripple สัญญาณบอกสถานะ ไอคอนต่างๆ สร้างบนเลเยอร์นี้เมื่อใช้ดีไซน์ Material ใน แอป

หลักการออกแบบ

หลักการสำคัญของ Jetpack Compose คือการนำเสนอไฟล์ ฟังก์ชันที่ประกอบ (หรือประกอบขึ้น) เข้าด้วยกัน แทนที่จะเป็นมากกว่า 2-3 อย่าง คอมโพเนนต์โมโนลิธ วิธีนี้มีข้อดีหลายประการ

ควบคุม

คอมโพเนนต์ระดับสูงกว่ามีแนวโน้มที่จะสร้างได้มากกว่า แต่จํากัดจํานวนการเข้าถึงโดยตรง ควบคุมทุกอย่างที่คุณมี หากต้องการการควบคุมที่มากขึ้น คุณสามารถ "เลื่อนลง" เพื่อใช้ ระดับล่าง

เช่น ถ้าต้องการทำให้สีของคอมโพเนนต์เคลื่อนไหว คุณอาจใช้ animateColorAsState API:

val color = animateColorAsState(if (condition) Color.Green else Color.Red)

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

val color = remember { Animatable(Color.Gray) }
LaunchedEffect(condition) {
    color.animateTo(if (condition) Color.Green else Color.Red)
}

API animateColorAsState ระดับสูงกว่าสร้างขึ้นจากระดับที่ต่ำกว่า API Animatable การใช้ API ระดับล่างจะซับซ้อนกว่าแต่ให้ผลลัพธ์มากกว่า เลือกระดับนามธรรมที่เหมาะกับความต้องการของคุณมากที่สุด

การปรับแต่ง

การประกอบคอมโพเนนต์ที่มีระดับสูงขึ้นจากองค์ประกอบที่ใช้สร้างสรรค์ขนาดเล็กจะช่วยให้ ปรับแต่งคอมโพเนนต์ได้ง่ายขึ้น ตัวอย่างเช่น ลองพิจารณา การใช้งาน จาก Button ที่อยู่ในเลเยอร์ Material มีดังนี้

@Composable
fun Button(
    // …
    content: @Composable RowScope.() -> Unit
) {
    Surface(/* … */) {
        CompositionLocalProvider(/* … */) { // set LocalContentAlpha
            ProvideTextStyle(MaterialTheme.typography.button) {
                Row(
                    // …
                    content = content
                )
            }
        }
    }
}

Button ประกอบขึ้นจากองค์ประกอบ 4 อย่าง ได้แก่

  1. วัสดุ Surface ระบุพื้นหลัง รูปร่าง การจัดการการคลิก ฯลฯ

  2. CompositionLocalProvider ซึ่งจะเปลี่ยนอัลฟ่าของเนื้อหาเมื่อปุ่มเปิดหรือปิดใช้งาน

  3. ProvideTextStyle ตั้งค่ารูปแบบข้อความเริ่มต้นที่จะใช้

  4. Row จะมีนโยบายเลย์เอาต์เริ่มต้นสำหรับเนื้อหาของปุ่ม

เราไม่ได้ใส่พารามิเตอร์และความคิดเห็นบางรายการเพื่อให้โครงสร้างชัดเจนขึ้น คอมโพเนนต์ทั้งหมดจะมีโค้ดประมาณ 40 บรรทัดเท่านั้น ประกอบ 4 องค์ประกอบนี้เพื่อใช้ปุ่ม คอมโพเนนต์ที่คล้ายกับ Button ทราบว่าจะต้องแสดงพารามิเตอร์ใดบ้าง และสร้างความสมดุลให้กับ การปรับแต่งที่พารามิเตอร์จำนวนมากที่อาจทำให้เป็นคอมโพเนนต์ ใช้งานได้ยากขึ้น คอมโพเนนต์เนื้อหา เช่น ข้อเสนอของการปรับแต่งที่ระบุไว้ เข้ากับระบบดีไซน์ Material ทำให้สามารถทำตามดีไซน์ Material ได้ง่ายๆ หลักการต่างๆ

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

@Composable
fun GradientButton(
    // …
    background: List<Color>,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Row(
        // …
        modifier = modifier
            .clickable(onClick = {})
            .background(
                Brush.horizontalGradient(background)
            )
    ) {
        CompositionLocalProvider(/* … */) { // set material LocalContentAlpha
            ProvideTextStyle(MaterialTheme.typography.button) {
                content()
            }
        }
    }
}

การใช้งานข้างต้นยังคงใช้คอมโพเนนต์จากเลเยอร์ Material เช่น แนวคิดของ Material อัลฟ่าเนื้อหาปัจจุบัน และรูปแบบข้อความปัจจุบัน แต่จะแทนที่วัสดุ Surface ด้วยองค์ประกอบ Row และจัดรูปแบบเพื่อให้ได้รูปลักษณ์ที่ต้องการ

หากคุณไม่ต้องการใช้แนวคิด Material เลย เช่น ถ้าการสร้าง ระบบออกแบบพิเศษของคุณเอง คุณสามารถเลื่อนลงเพื่อดูครีเอทีฟรองพื้น คอมโพเนนต์เลเยอร์ดังนี้

@Composable
fun BespokeButton(
    // …
    backgroundColor: Color,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Row(
        // …
        modifier = modifier
            .clickable(onClick = {})
            .background(backgroundColor)
    ) {
        // No Material components used
        content()
    }
}

Jetpack Compose จะสำรองชื่อที่ง่ายที่สุดสำหรับคอมโพเนนต์ระดับสูงสุด ตัวอย่างเช่น androidx.compose.material.Text สร้างขึ้นจาก androidx.compose.foundation.text.BasicText วิธีนี้ช่วยให้คุณสามารถติดตั้งใช้งาน ชื่อที่ค้นพบได้หากคุณต้องการแทนที่ระดับที่สูงขึ้น

การเลือกนามธรรมที่เหมาะสม

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

ตัวอย่างเช่น หากต้องการเพิ่มการรองรับท่าทางสัมผัสในคอมโพเนนต์ที่กำหนดเอง ให้ทำดังนี้ สามารถสร้างสิ่งนี้ขึ้นเองตั้งแต่ต้นโดยใช้ Modifier.pointerInput แต่ยังมีคอมโพเนนต์ระดับสูงอื่นๆ อีก อาจนำเสนอจุดเริ่มต้นที่ดีกว่า เช่น Modifier.draggable, Modifier.scrollable หรือ Modifier.swipeable

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

ดูข้อมูลเพิ่มเติม

โปรดดู ตัวอย่างของ Jetsnack ตัวอย่างของการสร้างระบบการออกแบบที่กำหนดเอง