Dalam arsitektur A2UI, setiap platform didorong oleh katalog komponen. Daripada membuat agen AI menciptakan primitif UI sendiri atau membuat kode arbitrer, katalog Anda mendeklarasikan komponen, skema properti, dan kemampuan yang tersedia untuk agen. Kemudian, agen menggunakan komponen ini untuk membuat antarmuka pengguna.
Saat membangun katalog kustom untuk sistem desain aplikasi, Anda
menerapkan komponen yang memetakan definisi katalog tersebut ke elemen UI Jetpack
Compose yang konkret. Setiap komponen A2UI (A2uiComponent) menentukan kontrak skema propertinya, mengevaluasi kesiapan saat data dinamis tiba, mengikat properti reaktif dari model data, memancarkan UI Compose, dan mengirimkan tindakan interaksi pengguna kembali ke agen.
Renderer UI Compose (androidx.a2ui.compose:compose-ui) menyediakan
cakupan penerima dan antarmuka yang diperlukan untuk menerapkan komponen kustom, yang
mengikuti sistem desain aplikasi Anda.
Mendeklarasikan properti komponen yang diketik secara statis
Sebelum merender, deklarasikan properti yang diharapkan komponen dari
agen. Lapisan runtime menyediakan API A2uiProperty yang diketik secara statis yang digunakan untuk pembuatan skema JSON dan mengekstrak nilai saat runtime:
// 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)
Menerapkan antarmuka A2uiComponent
Terapkan antarmuka A2uiComponent untuk menentukan skema komponen dan memetakan
properti yang diterima dari agen ke UI Compose:
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,
)
}
}
Menyelesaikan binding model data reguler dan dua arah
Implementasi komponen menggunakan A2uiComponentScope untuk menyelesaikan properti yang terikat secara dinamis. Untuk properti dinamis reguler, bind menampilkan nilai saat ini dan otomatis berlangganan pembaruan model data.
Untuk komponen input interaktif, bindUpdater menampilkan lambda updater yang stabil.
Jika agen memberikan string literal, bukan jalur data yang dapat ditulis, lambda
updater adalah null, yang menandakan bahwa kolom bersifat hanya baca:
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)
}
}
Mengirim tindakan pengguna ke agen
Komponen interaktif menggunakan A2uiComponentScope.dispatchAction untuk mengirim peristiwa pengguna kembali ke agen:
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)
}
}
}
}
Menangani komponen turunan dan rendering progresif
Komponen yang mendukung turunan bertingkat menggunakan observeA2uiComponentState(id) untuk
mengamati status turunan. Hal ini memungkinkan rendering progresif di mana penampung
induk merender shell-nya saat komponen turunan dimuat secara independen:
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)
}
}
}
Untuk menangani kumpulan atau daftar turunan (seperti item dalam kolom, baris, atau
daftar), deklarasikan properti menggunakan A2uiProperty.childList dan selesaikan
turunan dengan 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)
}
}
}
}
}
Mengintegrasikan rendering media native di Katalog Dasar
Saat menggunakan penerapan Katalog Dasar yang disediakan
(androidx.compose.material3:material3-a2ui), Anda dapat memasukkan library media pilihan Anda (seperti Coil untuk gambar atau ExoPlayer untuk video) ke komponen media
Katalog Dasar:
// 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) },
)
}
Detail implementasi
Bagian berikut menjelaskan emisi UI rekursif, evaluasi properti dinamis, dan pelaporan error.
Perjalanan pengguna implementasi komponen memperkenalkan API utama berikut:
A2uiComponent: Antarmuka yang menentukan metadata komponen, skema properti, pemeriksaan kesiapan (isReady), dan emisi rendering (Content).A2uiProperty: Deklarasi properti yang diketik secara statis yang digunakan untuk pembuatan skema JSON dan penyelesaian nilai runtime.A2uiComponentScope: Cakupan penerima yang menyediakan kemampuan kontekstual (seperti pengikatan data, pengiriman tindakan, dan pengamatan status turunan) ke implementasi komponen.A2uiComponentProperties: Penampung untuk properti komponen yang diterima dari agen yang menyediakan akses properti yang aman untuk jenis.A2uiComponentState: Merepresentasikan status penyelesaian pemuatan reaktif, keberhasilan, atau error komponen.
Emisi UI rekursif dan perutean dinamis
Status root yang diangkat oleh pemanggil (atau status komponen turunan yang diselesaikan dalam
komponen induk) memulai rendering komponen rekursif melalui fungsi composable A2uiComponent. Daripada menggabungkan status yang diselesaikan secara erat ke penerapan UI tertentu, fungsi ini bertindak sebagai perute dinamis.