ในสถาปัตยกรรม A2UI ทุกแพลตฟอร์มจะขับเคลื่อนโดยแคตตาล็อกคอมโพเนนต์ แคตตาล็อกจะประกาศคอมโพเนนต์ สคีมาพร็อพเพอร์ตี้ และความสามารถที่พร้อมใช้งานสำหรับ AI Agent แทนที่จะให้ AI Agent สร้างองค์ประกอบ UI ของตัวเองหรือสร้างโค้ดที่กำหนดเอง จากนั้น Agent จะใช้คอมโพเนนต์เหล่านี้เพื่อ สร้างอินเทอร์เฟซผู้ใช้
เมื่อสร้างแคตตาล็อกที่กำหนดเองสำหรับระบบการออกแบบของแอป คุณจะ
ใช้คอมโพเนนต์ที่แมปคำจำกัดความของแคตตาล็อกเหล่านั้นกับองค์ประกอบ UI ของ Jetpack
Compose ที่เฉพาะเจาะจง คอมโพเนนต์ A2UI แต่ละรายการ (A2uiComponent) จะกำหนดสัญญาของสคีมาพร็อพเพอร์ตี้ ประเมินความพร้อมเมื่อข้อมูลแบบไดนามิกมาถึง ผูกพร็อพเพอร์ตี้แบบรีแอกทีฟจากโมเดลข้อมูล ปล่อย UI ของ Compose และส่งการดำเนินการโต้ตอบของผู้ใช้กลับไปยังเอเจนต์
ตัวแสดงผล UI ของ Compose (androidx.a2ui.compose:compose-ui) มีอินเทอร์เฟซและขอบเขตตัวรับที่จำเป็นต่อการใช้คอมโพเนนต์ที่กำหนดเอง ซึ่งเป็นไปตามระบบการออกแบบของแอป
ประกาศพร็อพเพอร์ตี้ของคอมโพเนนต์ที่พิมพ์แบบคงที่
ก่อนที่จะแสดงผล ให้ประกาศพร็อพเพอร์ตี้ที่คอมโพเนนต์คาดหวังจากเอเจนต์ เลเยอร์รันไทม์มี A2uiProperty API ที่พิมพ์แบบคงที่ซึ่งใช้
ทั้งในการสร้างสคีมา JSON และการแยกค่าที่รันไทม์
// Define static properties, dynamic bindings, and component references
val textProp = A2uiProperty.dynamicString("text", required = true)
val variantProp = A2uiProperty.stringEnum("variant", enumValues = listOf("body", "title"))
val childProp = A2uiProperty.componentId("child", required = true)
val actionProp = A2uiProperty.action("action", required = true)
ใช้ส่วนติดต่อ A2uiComponent
ใช้A2uiComponentอินเทอร์เฟซเพื่อกำหนดสคีมาของคอมโพเนนต์และแมปพร็อพเพอร์ตี้ที่ได้รับจาก Agent ไปยัง Compose UI ดังนี้
object CustomTextComponent : A2uiComponent {
private val textProp = A2uiProperty.dynamicString("text", required = true)
private val variantProp = A2uiProperty.stringEnum(
"variant",
enumValues = listOf("body", "title"),
)
override val name = "Text"
override val description = "Displays dynamic text."
override val properties = listOf(textProp, variantProp)
@Composable
override fun A2uiComponentScope.isReady(properties: A2uiComponentProperties): Boolean {
// The component does not become ready until dynamic text data arrives
return properties.bind(textProp) != null
}
@Composable
override fun A2uiComponentScope.Content(
properties: A2uiComponentProperties,
modifier: Modifier,
) {
// Reactively resolve dynamic data binding and subscribe to updates
val text = properties.bind(textProp) ?: ""
// Read the static configuration property
val variant = properties[variantProp] ?: "body"
val textStyle = if (variant == "title") {
MaterialTheme.typography.titleLarge
} else {
MaterialTheme.typography.bodyLarge
}
Text(
text = text,
style = textStyle,
modifier = modifier,
)
}
}
แก้ไขการเชื่อมโยงโมเดลข้อมูลแบบปกติและแบบ 2 ทาง
การใช้งานคอมโพเนนต์ใช้ A2uiComponentScope เพื่อแก้ปัญหาพร็อพเพอร์ตี้ที่เชื่อมโยงแบบไดนามิก
สำหรับพร็อพเพอร์ตี้แบบไดนามิกปกติ bind จะแสดงค่าปัจจุบันและ
สมัครรับข้อมูลการอัปเดตโมเดลข้อมูลโดยอัตโนมัติ
สำหรับคอมโพเนนต์อินพุตแบบอินเทอร์แอกทีฟ bindUpdater จะแสดงผล Lambda ของโปรแกรมอัปเดตที่เสถียร
หากตัวแทนระบุสตริงตามตัวอักษรแทนเส้นทางข้อมูลที่เขียนได้ Lambda ของโปรแกรมอัปเดตจะnull ซึ่งเป็นการส่งสัญญาณว่าฟิลด์เป็นแบบอ่านอย่างเดียว
val labelProp = A2uiProperty.dynamicString("label", required = true)
val valueProp = A2uiProperty.dynamicBoolean("value")
@Composable
fun A2uiComponentScope.CustomCheckbox(properties: A2uiComponentProperties) {
// Read a dynamic property from the data model subscribing to updates
val label = properties.bind(labelProp) ?: ""
// Bind a property value and its updater to handle two-way data binding
val checked = properties.bind(valueProp) ?: false
val onCheckedChange = properties.bindUpdater(valueProp)
Row(verticalAlignment = Alignment.CenterVertically) {
Checkbox(
checked = checked,
onCheckedChange = onCheckedChange,
enabled = (onCheckedChange != null), // Read-only if no writable path was bound
)
Text(text = label)
}
}
ส่งการดำเนินการของผู้ใช้ไปยัง Agent
คอมโพเนนต์แบบอินเทอร์แอกทีฟใช้ A2uiComponentScope.dispatchAction เพื่อส่งเหตุการณ์ของผู้ใช้กลับไปยัง Agent
object CustomButtonComponent : A2uiComponent {
private val childProp = A2uiProperty.componentId("child", required = true)
private val actionProp = A2uiProperty.action("action", required = true)
override val name = "Button"
override val description = "A clickable button."
override val properties = listOf(childProp, actionProp)
@Composable
override fun A2uiComponentScope.Content(
properties: A2uiComponentProperties,
modifier: Modifier,
) {
val actionDefinition = properties[actionProp]
val childId = properties[childProp] ?: return
val currentAction by rememberUpdatedState(actionDefinition)
val onClick: () -> Unit = remember {
{ currentAction?.let { dispatchAction(it) } }
}
Button(onClick = onClick, modifier = modifier) {
val childState = observeA2uiComponentState(id = childId)
when (childState) {
is A2uiComponentState.Loading -> CircularProgressIndicator()
is A2uiComponentState.Error -> Text("Error")
is A2uiComponentState.Success -> A2uiComponent(childState.component)
}
}
}
}
จัดการคอมโพเนนต์ย่อยและการแสดงผลแบบค่อยๆ
คอมโพเนนต์ที่รองรับองค์ประกอบย่อยที่ซ้อนกันจะใช้ observeA2uiComponentState(id) เพื่อ
สังเกตสถานะขององค์ประกอบย่อย ซึ่งช่วยให้การแสดงผลแบบค่อยๆ เป็นค่อยๆ ไปทำงานได้ โดยที่คอนเทนเนอร์หลักจะแสดงผลเชลล์ในขณะที่คอมโพเนนต์ย่อยโหลดแยกกัน
val headerChildProp = A2uiProperty.componentId("headerId", required = true)
@Composable
fun A2uiComponentScope.CustomCompositeContent(
properties: A2uiComponentProperties,
) {
val headerId = properties[headerChildProp] ?: return
val headerState = observeA2uiComponentState(id = headerId)
when (headerState) {
is A2uiComponentState.Loading -> {
// Render a localized loading placeholder
LinearProgressIndicator()
}
is A2uiComponentState.Error -> {
// Render a localized error fallback
Text("Failed to load header")
}
is A2uiComponentState.Success -> {
// Forward the resolved child component to the visual UI router
A2uiComponent(headerState.component)
}
}
}
หากต้องการจัดการคอลเล็กชันหรือรายการขององค์ประกอบย่อย (เช่น รายการในคอลัมน์ แถว หรือ
รายการ) ให้ประกาศพร็อพเพอร์ตี้โดยใช้ A2uiProperty.childList และแก้ไข
องค์ประกอบย่อยด้วย bindChildReferences ดังนี้
val childrenProp = A2uiProperty.childList("children", required = true)
@Composable
fun A2uiComponentScope.CustomColumn(
properties: A2uiComponentProperties,
modifier: Modifier = Modifier,
) {
// Resolve child references (supports both static ID arrays and dynamic data templates)
val childReferences = properties.bindChildReferences(childrenProp) ?: return
Column(modifier = modifier) {
childReferences.forEach { reference ->
key(reference.id, reference.baseDataPath) {
val childState = observeA2uiComponentState(reference)
when (childState) {
is A2uiComponentState.Loading -> CircularProgressIndicator()
is A2uiComponentState.Error -> Text("Failed to load child")
is A2uiComponentState.Success -> A2uiComponent(childState.component)
}
}
}
}
}
ผสานรวมการแสดงผลสื่อแบบเนทีฟในแคตตาล็อกพื้นฐาน
เมื่อใช้การติดตั้งใช้งานแคตตาล็อกพื้นฐานที่ระบุไว้
(androidx.compose.material3:material3-a2ui) คุณจะเสียบไลบรารีสื่อที่ต้องการ (เช่น Coil สำหรับรูปภาพหรือ ExoPlayer สำหรับวิดีโอ) เข้ากับคอมโพเนนต์สื่อของแคตตาล็อกพื้นฐานได้โดยทำดังนี้
// Configure an Image component for the Basic Catalog using Coil
val coilImage = MaterialA2uiBasicCatalogV1Defaults.image { url, desc, scale, modifier, onError ->
AsyncImage(
model = url,
contentDescription = desc,
contentScale = scale,
modifier = modifier,
onError = { state -> onError(state.result.throwable) },
)
}
รายละเอียดการติดตั้งใช้งาน
ส่วนต่อไปนี้จะอธิบายการปล่อย UI แบบเรียกซ้ำ การประเมินพร็อพเพอร์ตี้แบบไดนามิก และการรายงานข้อผิดพลาด
เส้นทางของผู้ใช้ในการติดตั้งใช้งานคอมโพเนนต์จะแนะนำ API หลักต่อไปนี้
A2uiComponent: อินเทอร์เฟซที่กำหนดข้อมูลเมตาของคอมโพเนนต์ สคีมาพร็อพเพอร์ตี้ การตรวจสอบความพร้อม (isReady) และการปล่อยการแสดงผล (Content)A2uiProperty: การประกาศพร็อพเพอร์ตี้แบบกำหนดประเภทแบบคงที่ซึ่งใช้สำหรับสคีมา JSON และการสร้างค่ารันไทม์A2uiComponentScope: ขอบเขตตัวรับที่ให้ความสามารถตามบริบท (เช่น การเชื่อมโยงข้อมูล การส่งการดำเนินการ และการสังเกตสถานะขององค์ประกอบย่อย) ไปยัง การติดตั้งใช้งานคอมโพเนนต์A2uiComponentProperties: คอนเทนเนอร์สำหรับพร็อพเพอร์ตี้คอมโพเนนต์ที่ได้รับ จากเอเจนต์ที่ให้การเข้าถึงพร็อพเพอร์ตี้ที่ปลอดภัยตามประเภทA2uiComponentState: แสดงถึงสถานะการโหลดแบบรีแอกทีฟ ความสำเร็จ หรือข้อผิดพลาด ของคอมโพเนนต์
การปล่อย UI แบบเรียกซ้ำและการกำหนดเส้นทางแบบไดนามิก
สถานะรูทที่ผู้เรียกใช้ย้ายขึ้น (หรือสถานะคอมโพเนนต์ย่อยที่แก้ไขภายในคอมโพเนนต์หลัก) จะเริ่มการแสดงผลคอมโพเนนต์แบบเรียกซ้ำผ่านฟังก์ชันที่ประกอบกันได้ A2uiComponent ฟังก์ชันนี้ทำหน้าที่เป็นเราเตอร์แบบไดนามิกแทนที่จะเชื่อมโยงสถานะที่แก้ไขแล้วกับการใช้งาน UI ที่เฉพาะเจาะจง