適用于 Wear OS 的 Compose 已推出了 開發(fā)者預覽版跺株,使用 Compose 構建 Wear OS 應用间涵,不僅可以輕松遵循 Material You 指南纳鼎,同時可以將 Compose 的優(yōu)點發(fā)揮出來。開箱即用,幫助開發(fā)者使用更少的代碼快速構建出更精美的 Wear OS 應用。本文將通過 Wear Compose 主要的可組合項 (Composable) 來幫助您更好地了解如何使用 Compose 來進行構建扇谣。
如果您更喜歡通過視頻了解此內容,請 點擊此處 查看闲昭。
移動應用往往需要針對多種不同的界面種類進行開發(fā)罐寨,通常情況下,承載應用的主界面由 Fragment序矩、Activity 和 View 構成鸯绿,而在 Compose 的世界中則是由可組合項構成,作為開發(fā)者您需要了解并適應這種變化。除此之外瓶蝴,您還要針對額外的通知界面進行開發(fā)毒返,這意味著您需要在主應用界面之外提醒用戶注意某些重要信息,或讓他們在啟動主應用后繼續(xù)完成剛剛執(zhí)行的操作舷手,例如跟蹤跑步路線或者播放音樂拧簸。如果您使用了 Widget,也可以借助此類界面向用戶提供信息男窟。
Wear OS 擁有 多種界面盆赤,在打造完備的 Wear OS 應用體驗時,需要您全部考慮:
疊加層 (Overlay) 與移動應用的主界面類似歉眷,之前由 Activity弟劲、View 和 Fragment 組成,現在由可組合項構成姥芥,非常適合流程較長或較為復雜的交互;
通知 (Notification) 界面同樣符合移動應用開發(fā)準則汇鞭;
復雜功能 (Complication) 可在表盤中提供信息便于用戶直接查看凉唐,用戶只需在表盤上輕點一下,Complication 即可打開相關聯的應用霍骄,或執(zhí)行獨立操作台囱,例如飲水記錄功能,記錄您一天用水杯喝水的次數读整;
圖塊 (Tile) 提供了更多展示內容的空間簿训,用戶可在表盤上通過任意方向滑動,快速訪問信息米间、執(zhí)行操作强品。
本文我們將著重介紹 Overlay 界面,并快速演示幾項 Wear 可組合項屈糊,了解它們的工作原理及其與移動平臺的相似之處的榛。
添加依賴項
在使用 Wear Compose 之前,我們需要先確保已有正確的依賴項逻锐,它同移動版 Compose 略有不同夫晌。在移動版上,主要使用的依賴項有 Material昧诱、Foundation晓淀、UI、Runtime 和 Compiler盏档,您還可以選擇使用 Navigation 和 Animation 依賴凶掰。但在 Wear 中,您可以使用一樣的 UI 依賴項,Runtime锄俄、Compiler 和 Animation 也都是相同的局劲。此外,其他一些方面也都是相同的奶赠,比如工具和一些 Compose 設計理念鱼填,比如使用雙向數據流。
但還是有一些不同之處的毅戈,比如您需要使用 Wear Compose Material 替換 Material苹丸,單從技術上來說移動版 Material 也是可以直接用的,但它并沒有針對 Wear 的一些特性進行優(yōu)化苇经,類似的我們還推薦您使用 Wear Navigation 來替換 Navigation赘理。
雖然我們建議直接使用 Wear Compose Material,但您仍然可以使用 Material Ripple 和 Material Icons Extended 等扇单。在添加正確的 Wear 依賴項后商模,您就可以著手進行開發(fā)了。
在 Wear Compose 文檔頁面 查看依賴項蜘澜。
Wear OS Material 庫介紹
Compose Wear OS Material 庫 提供了很多與移動平臺上相同的可組合項施流,您可以替換 Material 主題,并且自定義顏色鄙信、字體等瞪醋,不同的是它們都針對手表進行了優(yōu)化。接下來我們就為您介紹一些常用的可組合項装诡。
Button
Button 屬于緊湊的界面元素银受,用戶可通過點按 Button 執(zhí)行操作或做出選擇。
通過如下代碼可輕松添加 Button鸦采,雖然樣式與移動版不同宾巍,但代碼一樣。我們在代碼里初始化了一個 Button 可組合項渔伯,然后聲明了一些參數蜀漆,它們被稱為 Modifier,通過 Modifier 可以更改很多屬性咱旱,比如這里的 onClick确丢、same、enabled吐限,若您還想為 Button 添加一個圖標鲜侥,那就需要用到包含 painter、contentDescription 和 modifier 的 Icon 可組合項:
Button(
modifier = Modifier.size (ButtonDefaults.LargeButtonSize),
onClick = {... },
enabled = enabledState
) {
Icon(
painter = painterResource (id = R.drawable.ic_phone),
contentDescription = "phone",
modifier = Modifier
.size(24. dp)
.wrapContentSize(align = Alignment.Center),
)
}
△ Button 可組合項代碼
通過上述代碼诸典,我們可以創(chuàng)建精美小巧的 Button描函,顯示效果如下:
Card
Card 可針對單一主題的內容和操作進行呈現,十分靈活。
如圖左側 Card 展示了一些圖標和文字舀寓,中間界面只保留了文字胆数,右側使用了一張圖片作為背景。
在 Wear OS 中互墓,主要有 AppCard 和 TitleCard 兩種 Card必尼,TitleCard 更側重文字展示,本文我們將著重介紹 AppCard篡撵。如下示例代碼創(chuàng)建了一個 AppCard判莉,并相繼通過 Image、Text 和 Column 定制內容:
AppCard(
appImage = {
Image(painter = painterResource(id = R.drawable.ic_message), …)
},
appName = { Text ("Messages") },
time = { Text ("12m" ) },
title = { Text("Kim Green") },
onClick = { … },
body = {
Column(modifier = Modifier.fillMaxWidth()) {
Text("On my way!")
}
},
)
△ Card 可組合項代碼
通過上述代碼育谬,我們創(chuàng)建出了一個精美的 Card券盅,顯示效果如下:
如需獲得不同外觀的精美卡片顯示效果,僅需對代碼進行輕微調整即可膛檀。
Chip
Chip 旨在實現快捷的一鍵操作锰镀,對屏幕空間有限的 Wear 設備尤其有用,各種 Chip 變體也能讓您盡情揮灑創(chuàng)意咖刃。
下面是實現 Chip 可組合項的代碼和實現效果互站,您會發(fā)現它十分易用,同上述的一些代碼也大致相似:
Chip(
modifier = Modifier.align(Alignment.CenterHorizontally),
onClick = { … },
enabled = enabledState,
label = {
Text(
text = "1 minute Yoga",
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
icon = {
Icon(
painter = painterResource (id = R.drawable.ic_yoga),
contentDescription= "yoga icon",
modifier = Modifier
.size(24.dp)
.wrapContentSize(align = Alignment.Center),
)
},
)
△ Chip 可組合項代碼
ToggleChip
ToggleChip 和 Chip 類似僵缺,區(qū)別是用戶使用單選按鈕、切換開關踩叭、復選框:
如下所示 ToggleChip 用例:
圖左展示磕潮,只需輕點即可開關聲音;圖右則對 ToggleChip 進行了拆分容贝,提供了兩個不同的可點擊區(qū)域自脯,通過右側按鈕可將其關閉,點擊左側可以進入應用以便對鬧鐘進行編輯斤富。其代碼大同小異:
ToggleChip(
modifier = Modifier.height(32.dp)
checked = checkedState,
onCheckedChange = { … }
label = {
Text(
text = "Sound",
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
)
△ ToggleChip 代碼
CurvedText 和 TimeText
CurvedText 專門針對了圓形屏幕進行了優(yōu)化膏潮,這對圓形設備來說非常重要,而 TimeText 是基于 CurvedText 所創(chuàng)建的可組合項满力,它為您處理時間方面的所有文字顯示工作焕参。
如下代碼示例展示了如何創(chuàng)建 TimeText,并以 CurvedText 的方式進行展示:
var textBeforeTime by rememberSaveable { mutableStateOf("ETA 99 hours") }
// 首先創(chuàng)建在時間之前顯示的前綴字符串
TimeText(
// 創(chuàng)建 TimeText 可組合項
leadingCurvedContent = {
BasicCurvedText(
text = textBeforeTime,
style = TimeTextDefaults.timeCurvedTextStyle()
)
},
// 指定 leadingCurvedContent油额,在時間文本前顯示文字叠纷,以 CurvedText 的方式在曲面設備上展示。
leadingLinearContent = {
Text(
text = textBeforeTime,
style = TimeTextDefaults.timeTextStyle()
)
},
// 指定 leadingLinearContent潦嘶,在時間文本前顯示文字涩嚣,常規(guī)顯示,適用于非曲面設備。
)
△ TimeText 代碼
通過上述代碼航厚,我們可以看到時間文本在圓形屏幕的顯示效果如下:
ScalingLazyColumn
列表幾乎是每個應用中都會用到的組件顷歌,它縱向展示了連續(xù)的界面元素。但由于 Wear OS 手表設備的屏幕頂部和底部空間都非常小幔睬,因此 Material Design 引入了新的 ScalingLazyColumn 來進行縮放和透明度的展示眯漩,這樣有助于您在較小的空間內查看列表的內容。
△ ScalingLazyColumn 顯示效果
上圖展示了 ScalingLazyColumn 的效果溪窒,您可以看到隨著列表內元素的滑入坤塞,當列表的某一行靠近中心位置時,會放大到完整尺寸澈蚌,而隨著該元素的滑出摹芙,會變得越來越小 (并且變得更透明) 直至完全消失,這種效果十分有利于內容的展示宛瞄,內容更易于用戶閱讀浮禾。
ScalingLazyColumn 底層是由 LazyColumn 實現的,它只會對即將要在屏幕上呈現的內容進行處理份汗,這樣能夠高效地處理大量數據盈电,且能夠以縮放和透明效果進行展示,因此它應該成為 Wear OS 的默認組件杯活。
如上效果匆帚,ScalingLazyColumn 代碼示例如下:
val scalingLazyListState: ScalingLazyListState = rememberScalingLazyListState()
ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(6.dp),
state = scalingLazyListState,
) {
items(messageList.size) { message ->
Card(...) {...}
}
item {
Card(...) {...}
}
}
△ ScalingLazyColumn 示例代碼
SwipeToDismissBox
這是大家十分熟悉的 Box 組件被視為界面中的一個容器,可在移動端使用旁钧,但 Wear 中有專屬版本 SwipeToDismissBox吸重,可用于您的布局,顧名思義它的功能是滑動以關閉歪今。在 Wear OS 中嚎幸,主要的手勢就是滑動,通過使用 SwipeToDismissBox 向右滑動,就相當于點擊了 "返回" 按鈕。
val state = rememberSwipeToDismissBoxState()
SwipeToDismissBox(
state = state,
){ isBackground ->
if (isBackground) {
Box(modifier = Modifier. fillMaxSize().background(MaterialTheme.colors.secondaryVariant))
} else {
Column(
modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.primary),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
){
Text ("Swipe to dismiss", color = MaterialTheme.colors.onPrimary)
}
}
}
△ SwipeToDismissBox 示例代碼
在上述代碼中余掖,我們?yōu)?SwipeToDismissBox 設置了 state 屬性,這一點和移動端不同替废。通過傳遞的 state 獲取到 isBackground 回調值,它代表了此過程是否是滑動返回泊柬,您可以根據不同的狀態(tài)展示不同的內容舶担。下圖是最終的呈現效果:
至此,我們介紹了一些 Wear OS 的可組合項彬呻,若您對移動端的可組合項開發(fā)有所了解衣陶,您可能會發(fā)現在 Wear OS 中開發(fā)基本是一樣的柄瑰,換句話說,您之前學習 Compose 時掌握的知識可以直接用于 Wear OS 開發(fā)剪况。
使用 Scaffold
Scaffold 可讓您實現具有基本 Material Design 布局結構的界面教沾,它可為最常見的頂層 Material 組件 (例如 TopBar、BottomBar译断、FloatingActionButton 和 Drawer) 提供槽位授翻。使用 Scaffold 時,您可以確保這些組件能夠正確放置并協同工作孙咪。而在 Wear OS 中堪唐,它也有著專屬的版本,除了同移動版相同的 content 組件之外翎蹈,額外提供了以下三個主要組件:
TimeText: 可以將時間置于屏幕的頂部淮菠,我們已經介紹過它,具體請參考上文中關于 TimeText 的部分荤堪;
Vignette: 可在屏幕周圍為您提供漂亮的暈影效果合陵,如上圖中所示;
PositionIndicator: 也稱為滾動指示器澄阳,是屏幕右側的指示符拥知,用于根據您傳入的狀態(tài)對象類型顯示當前指示符的位置。將它放置于 Scaffold 中是由于屏幕是弧形的碎赢,因此位置指示器需要位于表盤中央 (Scaffold)低剔,而不僅僅是在視口 (viewport) 中央。否則肮塞,指示器可能會被截斷襟齿。
Scaffold 設計
在進行 Scaffold 的設計時,請參考上圖中的層級順序進行考慮峦嗤,首先要做的是對 App 進行設置,其次是設置 MaterialTheme 來自定義一些應用的外觀和風格屋摔,緊接著是考慮如何放置 Scaffold烁设,最后才是對 Content 的定義。這個順序同在移動端是一樣的钓试,先考慮設置 Theme装黑,再到 Scaffold,接下來看一下如何編寫代碼:
// positionIndicator 在 Content 之外弓熏,因此要將 state 提升到 Scaffold 之上的級別
val scalingLazyListState: ScalingLazyListState = rememberScalingLazyListState()
MaterialTheme {
Scaffold(
modifier = Modifier.fillMaxSize(),
timeText = {...},
vignette = {
Vignette (vignettePosition =
VignettePosition.TopAndBottom)
},
positionIndicator = {
// 通過查看 state 來判斷是否處于滾動狀態(tài)恋谭,若否,則不會進行展示
if (scalingLazyListState.isScrollInProgress) {
// PositionIndicator 需要用到 state挽鞠,這也是我們從 LazyColumn 提升狀態(tài)的主要原因
PositionIndicator(scalingLazyListState =
scalingLazyListState)
}
}
) {
// 設置 content
…
}
}
△ Scaffold 示例代碼
上述代碼中疚颊,由于 positionIndicator 位于 content 之外狈孔,因此要將 state 提升到 Scaffold 之上的級別,來避免它在屏幕中被截斷材义。而在滾動時均抽,可以通過檢查滾動狀態(tài),通過隱藏時間顯示來為屏幕留出更多的空間其掂,還可以根據狀態(tài)來關閉或打開 vignette 效果油挥。positionIndicator 支持多種滾動選項,本例中我們使用了 scalingLazyListState款熬,還可以使用很多效果炫酷的其他選項深寥,具體請參考相關文檔。而關于 modifier 和 TimeText 想必不用過多介紹了贤牛,而 vignette 的設置其實也很簡單惋鹅。
Navigation
在本文一開始就提到您需要使用 Wear Navigation 依賴項來替換 Navigation,這里再次強調一下盔夜,從技術層面來說您仍可使用 Navigation负饲,但是可能會遇到各種問題,所以還是建議您直接使用已針對 Wear 優(yōu)化的 Wear Navigation喂链。
關于 Navigation 的設計返十,同 Scaffold 大致相同,采用了和移動版相同的設計椭微,只是在 Scaffold 之下和 Content 之上增加了 SwipeDismissableNavHost洞坑,顧名思義該組件支持滑出操作,您可以直接使用與移動應用開發(fā)相同的知識來編寫代碼蝇率。
MaterialTheme {
Scaffold(...){
val navController = rememberSwipeDismissableNavController()
SwipeDismissableNavHost(
navController = navController,
startDestination = Screen.MainScreen.route
) {
composable(route = Screen.MainScreen.route){
MyListScreen(...)
}
composable(route = Screen.DetailsScreen. route + "/{$ID}", ...) {
MyDetailScreen(...)
}
}
}
}
△ Navigation 示例代碼
在上述代碼中迟杂,MaterialTheme 和 Scaffold 與之前一樣,但我們創(chuàng)建了一個 navController本慕,并使用了 SwipeDismissable 版本的 rememberSwipeDismissableNavController排拷,名稱非常拗口,但是很容易理解它的功能锅尘。然后使用了 SwipeDismissableNavHost 將 startDestination 及其路徑傳遞到控制器中监氢,再設置主屏幕內容即可。您會發(fā)現代碼基本上同移動端相同藤违,非常便于理解浪腐。
總結
在 Wear OS 中,請確保使用合適的依賴項顿乒,替換 Material 并添加 Foundation 依賴议街,如果使用的是 Navigation 同樣也要進行替換。另外璧榄,所有 Compose 構建方面的知識都可以直接應用于 Wear Compose 中特漩,用移動端的開發(fā)經驗助您快速構建精美的 Wear 界面吧雹。
如需了解更多詳細信息,請參閱:
- 歡迎體驗 | Wear OS 版 Compose 開發(fā)者預覽版
- Wear OS 概覽
- 文檔指南: 在 Wear OS 上使用 Jetpack Compose
- Codelab: 開始編寫適用于 Wear OS 的應用
- GitHub 示例
歡迎您 點擊這里 向我們提交反饋拾稳,或分享您喜歡的內容吮炕、發(fā)現的問題。您的反饋對我們非常重要访得,感謝您的支持龙亲!