วิศวกรซอฟต์แวร์
ในเซสชันนี้ เราจะเจาะลึกด้านเทคนิคเกี่ยวกับวิวัฒนาการจากไทล์แบบเต็มหน้าจอไปสู่วิดเจ็ต Wear แบบไดนามิกที่มีความสูงบางส่วน
เราจะสำรวจประโยชน์ทางเทคนิคของ Jetpack Glance API แบบรวมและเครื่องมือ Remote Compose ใหม่ โดยจะอธิบายรายละเอียดว่า API และเครื่องมือดังกล่าวสอดคล้องกับ Modern Android Development อย่างไร
สุดท้าย เราจะแชร์คำแนะนำล่าสุดเกี่ยวกับ UX และอธิบายกลยุทธ์การย้ายข้อมูลที่นำไปใช้ได้จริงเพื่อช่วยคุณสร้าง UI ที่แสดงข้อมูลแบบคร่าวๆ รุ่นถัดไป
ข้อความถอดเสียง
สวัสดีทุกคน
ผมชื่อ Frank เป็นหัวหน้าทีมประสบการณ์การใช้งานที่เข้าถึงได้ง่ายสำหรับ Wear OS
นาฬิกาเป็นอุปกรณ์สำคัญที่อยู่ติดตัวผู้ใช้ตลอดทั้งวัน และวันนี้เราจะแนะนำวิธีใช้ประโยชน์จากประสบการณ์การใช้งานวิดเจ็ต Wear ใหม่
เราจะมาดู 2 วิธีในการนำเสนอฟีเจอร์ที่สำคัญที่สุดไปยังข้อมือของผู้ใช้โดยตรง และเราจะทำเช่นนั้นโดยใช้ API ล่าสุด ซึ่งเป็น API ที่ออกแบบมาเพื่อให้นักพัฒนาแอป Android เช่นคุณรู้สึกคุ้นเคยและใช้งานง่ายเมื่อสร้างแอปสำหรับ Wear OS
วันนี้ Yuri เพื่อนร่วมงานและเพื่อนร่วมโต๊ะที่ยอดเยี่ยมของฉันจะมาร่วมพูดคุยบนเวทีเพื่อเจาะลึกถึงวิธีการที่เรากำลังทำสิ่งนี้
สวัสดี ฉันชื่อ Yuri Schimke
ฉันเป็นวิศวกรในทีม Wear OS และงานหลักของฉันคือการทำงานกับ Remote Compose API
วันนี้ผมจะมาแนะนำ Remote Compose ให้คุณรู้จักคร่าวๆ และพูดถึงกระบวนการย้ายข้อมูลจากไทล์ไปยังวิดเจ็ต Wear ด้วย
หลังจากนี้ฉันจะขอส่งต่อให้ Frank
เยี่ยมมาก
ขอบคุณ Yuri
เรากำลังนำข้อมือเข้าสู่ระบบนิเวศวิดเจ็ต Android แบบรวมอย่างเป็นทางการด้วยการพัฒนาการ์ดแบบเต็มหน้าจอให้เป็นวิดเจ็ต Wear
และเนื่องจากตอนนี้ประสบการณ์เหล่านี้สร้างขึ้นโดยใช้ Jetpack Glance คุณจึงเห็นได้ว่าประสบการณ์เหล่านี้ใช้โครงสร้างพื้นฐานเดียวกันกับที่ใช้สำหรับวิดเจ็ตบนอุปกรณ์เคลื่อนที่
คุณจะเห็นว่าการสร้างแอปสำหรับ Wear จะยิ่งคุ้นเคยมากขึ้นในอนาคต
เรากำลังพัฒนา UI Stack ซึ่งเป็น UI Stack พื้นฐานเพื่อขับเคลื่อนประสบการณ์เหล่านี้
วิดเจ็ต Wear ทำงานด้วย Remote Compose ซึ่งเป็นเฟรมเวิร์ก Android ใหม่สำหรับ UI ระยะไกล
ซึ่งหมายความว่าในที่สุดคุณก็สามารถใช้ API แบบประกาศที่สอดคล้องกับ Jetpack Compose อย่างใกล้ชิดเพื่อสร้างประสบการณ์การใช้งานนอกแอป เช่น วิดเจ็ต
Remote Compose นำกราฟิกที่ทันสมัยและเลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์มาสู่วิดเจ็ต พร้อมทั้งรับประกันประสิทธิภาพและการประหยัดแบตเตอรี่
และด้วยการแยก UI ออกจากกระบวนการของแอปพลิเคชัน วิดเจ็ตของคุณจะได้รับการจัดเรียงอย่างปลอดภัยและแสดงผลได้ง่ายในอุปกรณ์ Android ที่หลากหลาย ขออภัย
ซึ่งหมายความว่าคุณสามารถนำประสบการณ์ที่สมบูรณ์นี้มาใช้กับแพลตฟอร์มระยะไกลที่รองรับเกือบทั้งหมดได้โดยใช้ความพยายามเพียงเล็กน้อย
เราจะเปิดตัวคอนเทนเนอร์ขนาดมาตรฐาน 2 ขนาด ได้แก่ ขนาดเล็กและขนาดใหญ่ เพื่อให้การเปลี่ยนจากไทล์เป็นวิดเจ็ตเป็นไปอย่างราบรื่นที่สุด
โดยออกแบบมาให้สอดคล้องกับรูปแบบวิดเจ็ตแบบตารางขนาด 2x1 และ 2x2 ที่คุณคุ้นเคยจากการสร้างวิดเจ็ตบนอุปกรณ์เคลื่อนที่
และตั้งแต่ Wear 7 เป็นต้นไปในผู้ผลิตฮาร์ดแวร์บางราย วิดเจ็ต Wear จะอยู่ในถาดที่เลื่อนในแนวตั้งควบคู่ไปกับวิดเจ็ตของแอปอื่นๆ
ตัวอย่างเช่น วิดเจ็ต Wear บน Samsung Galaxy Watch จะมีลักษณะดังนี้
สถาปัตยกรรมของแพลตฟอร์มสร้างขึ้นเพื่อรองรับถาดที่เลื่อนในแนวตั้ง เนื่องจากผู้ผลิตฮาร์ดแวร์จะใช้รูปแบบนี้ในอนาคต
และคุณไม่ต้องกังวลว่าจะทิ้งผู้ใช้หรือผู้ใช้เดิมไว้ข้างหลัง
API ของ Jetpack Glance Wear ออกแบบมาเพื่อรองรับความเข้ากันได้แบบย้อนหลังจนถึง Wear 4
ในอุปกรณ์รุ่นเก่าที่รองรับถาดวิดเจ็ตหรือการ์ดแนวนอน ระบบจะจัดการให้คุณ
คุณเพียงแค่ต้องระบุในโค้ดว่าต้องการรองรับโหมดเต็มหน้าจอ แล้วระบบจะนำวิดเจ็ตขนาดใหญ่ของคุณไปแสดงเป็นไทล์แบบดั้งเดิมแบบเต็มหน้าจอในอุปกรณ์เหล่านี้
ตอนนี้ Yuri จะอธิบายโค้ดเพื่อแสดงวิธีที่แน่นอนในการย้ายข้อมูลการ์ดที่มีอยู่ไปยังวิดเจ็ต Wear
ขอบคุณ Frank
หากคุณสร้างวิดเจ็ต Wear ใหม่ตั้งแต่ต้น ก็ทำได้ง่ายๆ
คุณเพียงแค่กำหนด GlanceWearWidgetService และสร้าง UI โดยใช้ Remote Compose
แต่หากแอปของคุณมีไทล์ในเวอร์ชันที่ใช้งานจริงอยู่แล้ว คุณจะต้องย้ายข้อมูลทั้งบริการ, UI และข้อมูล
เราได้จัดเตรียมแนวทางเพื่อให้ผู้ใช้ปัจจุบันได้รับประสบการณ์การใช้งานที่ราบรื่นที่สุดเท่าที่จะเป็นไปได้ในการเปลี่ยนผ่านนี้
สำหรับบริการ คุณสามารถเลือกกลยุทธ์ได้ 2 แบบ ได้แก่ บริการคู่หรือบริการเดียว
เราขอแนะนำให้ใช้บริการแบบคู่ ซึ่งคุณจะเก็บ TileService ที่มีอยู่ไว้สำหรับอุปกรณ์ Wear OS รุ่นเก่า และเพิ่ม GlanceWearWidgetService ใหม่สำหรับ Wear OS 7 ขึ้นไป
ในทางตรงกันข้าม เมื่อใช้บริการเดียว คุณจะสร้าง GlanceWearWidgetService ใหม่รายการเดียวที่จัดการทั้ง 2 โปรโตคอล โดยทำงานในโหมดความเข้ากันได้เพื่อแปลวิดเจ็ตเป็นไทล์ในอุปกรณ์รุ่นเก่า
ไม่ว่าจะเลือกวิธีใด คุณก็จะยังคงเข้าถึงผู้ใช้ได้
แต่จะส่งผลต่อโค้ดที่คุณจะใช้ต่อไปเท่านั้น
และเราเพิ่งเผยแพร่เอกสารประกอบที่ครอบคลุมในเว็บไซต์นักพัฒนาแอป Android ซึ่งให้รายละเอียดเกี่ยวกับวิธีและเวลาที่ควรใช้ทั้ง 2 รูปแบบนี้อย่างชัดเจน
ลองดูเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับกลยุทธ์ที่เหมาะกับแอปของคุณมากที่สุด
ทีนี้เรามาพูดถึงเลเยอร์ UI กัน
เมื่อย้ายข้อมูล โมเดลความคิดของคุณจะเปลี่ยนไป
ก่อนหน้านี้ การ์ดใช้ ProtoLayout ซึ่งเป็นไลบรารีที่สร้างขึ้นสำหรับการ์ด Wear โดยเฉพาะ
ในทางกลับกัน Wear Widgets จะสร้างขึ้นจาก Remote Compose
Remote Compose ใช้พื้นฐานเดียวกันกับ Jetpack Compose UI โดยใช้คอมไพเลอร์หลักและรันไทม์เดียวกัน
และได้รับการออกแบบมาให้คุ้นเคย โดยใกล้เคียงกับไลบรารี Compose UI เช่น Foundation และ Material 3
แต่แทนที่จะเป็นกล่อง คอลัมน์ หรือข้อความ คุณจะสลับเป็น RemoteBox, RemoteColumn หรือ RemoteText แทน
ในกรณีนี้ คุณจะคุ้นเคยกับการเขียน "remote" เป็นอย่างมาก
นอกจากนี้ เพื่อเพิ่มความเร็วให้เวิร์กโฟลว์ เราได้รวมคอมโพเนนต์ Remote Compose ที่พร้อมใช้งานครบชุดซึ่งออกแบบมาสำหรับวิดเจ็ตโดยเฉพาะ
ซึ่งหมายความว่าคุณสามารถปรับการออกแบบ Wear Material 3 ที่มีอยู่ รวมถึงสิ่งที่คุณมีใน Figma ให้เข้ากับวิดเจ็ตได้อย่างง่ายดาย ทำให้ทั้งวิศวกรและนักออกแบบคุ้นเคยกับวิดเจ็ตนี้
มาดูตัวอย่างที่ชัดเจนกันว่าเฟรมเวิร์กเหล่านี้สอดคล้องกันอย่างไร
เราจะสร้างคอมโพเนนต์แอปของเราเองบนปุ่มสวิตช์ Material 3
ทางด้านซ้าย เรามีคอมโพเนนต์ Wear Compose มาตรฐานที่คุณจะเขียนภายในแอป และทางด้านขวาคือเวอร์ชัน Remote Compose
// Wear Compose
@Composable
fun HistoryEraserButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
checked: Boolean = true,
onCheckedChange: (Boolean) -> Unit = {},
)// Wear RemoteCompose
@RemoteComposable
@Composable
fun RemoteHistoryEraserButton(
onClick: Action,
modifier: RemoteModifier = RemoteModifier,
checked: RemoteBoolean = true.rb,
onCheckedChange: Action = Action.Empty,
)สังเกตว่ารูปแบบมีความคล้ายกันอย่างไร
อย่างไรก็ตาม เมื่อเขียนโค้ด Remote Compose คุณจะใช้คำอธิบายประกอบ RemoteComposable
และในกรณีที่คุณใช้พารามิเตอร์ที่คุ้นเคย เช่น ตัวแก้ไขและบูลีน พารามิเตอร์เหล่านั้นจะมีประเภท Remote Compose ที่เทียบเท่ากัน เช่น RemoteModifier และ RemoteBoolean
และคุณใช้การดำเนินการแทน Lambda
คุณจะต้องประกาศการดำเนินการ เช่น การเปิดใช้งาน Intent รอดำเนินการ การเรียกใช้บริการ หรือการอัปเดตค่า แทนที่จะกำหนดการเรียกใช้โค้ดเพื่อเรียกใช้ภายใน Lambda
มาดูที่ส่วนเนื้อหาของคอมโพเนนต์กัน
คุณจะสังเกตเห็นว่าแม้แต่ธีมก็สอดคล้องกัน
คุณสามารถใช้รูปแบบสี Material 3 กับคอมโพเนนต์ Remote Compose ได้เหมือนกับที่ทำใน Compose มาตรฐาน ซึ่งจะช่วยให้วิดเจ็ตตรงกับการสร้างแบรนด์ของแอปทุกประการ
// Wear Compose
val colors = MaterialTheme.colorScheme
SplitSwitchButton(
checked = checked,
onCheckedChange = onCheckedChange,
toggleContentDescription = "Arm Eraser",
onContainerClick = onClick,
modifier = modifier.fillMaxWidth(),
colors =
SwitchButtonDefaults.splitSwitchButtonColors(
checkedContainerColor = colors.errorContainer, …
),
label = { Text("History Eraser") },
secondaryLabel = { Text("Don't touch it!") },
)// Wear RemoteCompose
val colors = RemoteMaterialTheme.colorScheme
RemoteSplitSwitchButton(
checked = checked,
onCheckedChange = onCheckedChange,
toggleContentDescription = "Arm Eraser".rs,
onContainerClick = onClick,
modifier = modifier.fillMaxWidth(),
colors =
RemoteSplitSwitchButtonDefaults.splitSwitchButtonColors(
checkedContainerColor = colors.errorContainer, …
),
label = { RemoteText("History Eraser".rs) },
secondaryLabel = { RemoteText("Don't touch it!".rs) },
)และสุดท้าย มาดูผลลัพธ์กัน UI ที่ได้จะเป็นไปตามที่คุณคาดหวัง นั่นคือปุ่มเปิด/ปิดแบบเคลื่อนไหวที่สะอาดตาและตอบสนองได้ดี
บอกความแตกต่างได้ยาก
เราได้เพิ่มการรองรับตัวอย่าง Compose สำหรับวิดเจ็ต Wear เพื่อช่วยคุณออกแบบ ทำซ้ำ และยืนยันเลย์เอาต์วิดเจ็ตโดยไม่ต้องเรียกใช้ในอุปกรณ์หรือโปรแกรมจำลองเสมอไป
เนื่องจากวิดเจ็ตขึ้นอยู่กับข้อจำกัดของคอนเทนเนอร์เฉพาะ OEM เช่น ขนาด รูปร่าง ระยะห่างจากขอบ รัศมีมุม เราจึงสร้างขึ้นจากการกำกับเนื้อหาตัวอย่างมาตรฐาน แต่มีผู้ให้บริการพารามิเตอร์เพิ่มเติมเพื่อแทรกการกำหนดค่าเหล่านี้
@Preview(name = "Squircle Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetSquirclePreview(
@PreviewParameter(SquircleAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)
@Preview(name = "Round Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetRoundPreview(
@PreviewParameter(RoundAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)ไลบรารีนี้มีการติดตั้งใช้งานผู้ให้บริการ PreviewParameter ที่กำหนดค่าไว้ล่วงหน้าซึ่งตรงกับอุปกรณ์ Wear OS จริง
คำอธิบายประกอบตัวอย่างเหล่านี้ช่วยให้คุณยืนยันเลย์เอาต์ในรูปทรงฮาร์ดแวร์ที่แตกต่างกันได้อย่างง่ายดาย
นอกจากการทดสอบเลย์เอาต์แล้ว คุณยังใช้ตัวอย่างเหล่านี้เพื่อสร้างภาพนิ่งที่จำเป็นสำหรับเครื่องมือเลือกวิดเจ็ตในอุปกรณ์ได้ด้วย
@Preview(name = "Widget Picker Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetRectangularPreview(
@PreviewParameter(RectangularAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)ไลบรารีมีพารามิเตอร์สำหรับรูปร่างตัวอย่างสี่เหลี่ยมผืนผ้าซึ่งจำลองคอนเทนเนอร์สี่เหลี่ยมผืนผ้าแบบเต็มที่ไม่มีการครอบตัด
และหากต้องการดูว่าฟีเจอร์นี้ทำงานอย่างไรในทางปฏิบัติ เราได้อัปเดตแอปตัวอย่างสื่อ Horologist ให้มีวิดเจ็ตใหม่ที่แสดง API นี้
เราจะแสดงวิธีเพิ่มวิดเจ็ตสื่อทั้งเวอร์ชันขนาดเล็กและขนาดใหญ่
ดูข้อมูลใน GitHub เพื่อดูการเปรียบเทียบโค้ด ProtoLayout เดิมกับการติดตั้งใช้งาน Remote Compose ใหม่แบบเทียบกัน
เรามั่นใจว่าคุณจะต้องชอบ
สุดท้ายนี้ เราคงจะพูดว่ามีการพัฒนานวัตกรรมได้ไม่เต็มปากหากไม่พูดถึง AI
เราได้เผยแพร่ทักษะการย้ายข้อมูลจากไทล์ไปยังวิดเจ็ตใหม่บน GitHub เพื่อเร่งการย้ายข้อมูลของคุณ
คุณสามารถใช้เครื่องมือนี้กับ Agent ที่คุณเลือก หรือจะเพิ่มลงใน Android Studio โดยตรงโดยใช้ Android CLI ก็ได้
android skills add wear-widgets
android skills add --allตอนนี้เราจะส่งต่อให้ François สรุปสิ่งต่างๆ
ขอขอบคุณ
ขอบคุณ Yuri
ดังนั้นคุณจะเห็นได้ว่าการพัฒนาการ์ดให้เป็นวิดเจ็ต Wear เป็นกระบวนการที่ตรงไปตรงมามาก
สรุปคือ การย้ายข้อมูลไปยัง Jetpack Glance Wear และ Remote Compose ไม่ใช่แค่การอัปเดต UI เท่านั้น แต่เป็นการเข้าร่วมระบบนิเวศวิดเจ็ต Android ที่เป็นหนึ่งเดียวกันมากขึ้น
ตอนนี้คุณสามารถสร้างวิดเจ็ตที่สวยงามและเข้ากันได้แบบย้อนหลังโดยใช้เฟรมเวิร์กแบบประกาศที่ทันสมัยซึ่งสอดคล้องกับ Jetpack Compose อย่างสมบูรณ์
คุณเริ่มย้ายข้อมูลการ์ดได้ตั้งแต่วันนี้ โดยไปที่คู่มือเริ่มต้นใช้งานในเว็บไซต์ของนักพัฒนาแอป Android เพื่อดูรายละเอียดทั้งหมด
เราหวังว่าจะได้เห็นประสบการณ์การใช้งานที่แสดงออกถึงตัวตนและเข้าใจง่ายทั้งหมดที่คุณสร้างขึ้นสำหรับข้อมือ
ขอขอบคุณเป็นอย่างยิ่งที่มาร่วมงานกับเราในวันนี้ และหวังว่าคุณจะเพลิดเพลินกับช่วงเวลาที่เหลือของการประชุม
ขอขอบคุณ
// Wear Compose
@Composable
fun HistoryEraserButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
checked: Boolean = true,
onCheckedChange: (Boolean) -> Unit = {},
)// Wear RemoteCompose
@RemoteComposable
@Composable
fun RemoteHistoryEraserButton(
onClick: Action,
modifier: RemoteModifier = RemoteModifier,
checked: RemoteBoolean = true.rb,
onCheckedChange: Action = Action.Empty,
)// Wear Compose
val colors = MaterialTheme.colorScheme
SplitSwitchButton(
checked = checked,
onCheckedChange = onCheckedChange,
toggleContentDescription = "Arm Eraser",
onContainerClick = onClick,
modifier = modifier.fillMaxWidth(),
colors =
SwitchButtonDefaults.splitSwitchButtonColors(
checkedContainerColor = colors.errorContainer, …
),
label = { Text("History Eraser") },
secondaryLabel = { Text("Don't touch it!") },
)// Wear RemoteCompose
val colors = RemoteMaterialTheme.colorScheme
RemoteSplitSwitchButton(
checked = checked,
onCheckedChange = onCheckedChange,
toggleContentDescription = "Arm Eraser".rs,
onContainerClick = onClick,
modifier = modifier.fillMaxWidth(),
colors =
RemoteSplitSwitchButtonDefaults.splitSwitchButtonColors(
checkedContainerColor = colors.errorContainer, …
),
label = { RemoteText("History Eraser".rs) },
secondaryLabel = { RemoteText("Don't touch it!".rs) },
)@Preview(name = "Squircle Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetSquirclePreview(
@PreviewParameter(SquircleAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)
@Preview(name = "Round Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetRoundPreview(
@PreviewParameter(RoundAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)@Preview(name = "Widget Picker Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetRectangularPreview(
@PreviewParameter(RectangularAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)android skills add wear-widgets
android skills add --all