在前一章節(jié)的實現(xiàn)中,Skeleton: Main structure,我們留下了幾個 Jetpack 架構(gòu)組件,這些組件將在本章中使用,例如 Composables、ViewModels、Navigation 和 Hilt。此外,我們還通過 Scaffold 集成了 TopAppBar 和 BottomAppBar UI 模式的基本結(jié)構(gòu)。為了繼續(xù)改進(jìn)這個實現(xiàn),我們需要添加一個新的關(guān)鍵選項卡,即“應(yīng)用程序的通用狀態(tài)”。
App程序的狀態(tài):一般狀態(tài)
定義導(dǎo)航地圖
導(dǎo)航從其他UI元素
摘要
應(yīng)用程序狀態(tài):一個通用狀態(tài)
?“設(shè)計原則”中我們討論了狀態(tài)在現(xiàn)代 Android 應(yīng)用程序中的重要作用。
設(shè)計中可能存在三種類型的狀態(tài):屬性 UI 狀態(tài)、組件 UI 狀態(tài)和屏幕 UI 狀態(tài)。
除了這些狀態(tài)之外,我們還可以定義一種新類型的狀態(tài),即應(yīng)用程序狀態(tài)。
這個新狀態(tài)將定義應(yīng)用程序的通用狀態(tài)。它將用于屏幕之間的導(dǎo)航、自發(fā)消息(snackbars)的呈現(xiàn)以及應(yīng)用程序中其他可用的進(jìn)程。
在主目錄中,我們將定義一個名為 OrderNowState 的類,它將是我們的狀態(tài)持有者,代表這種類型的狀態(tài)。
接下來,我們像這樣執(zhí)行OrderNowState的初始實現(xiàn):
@SuppressLint("RememberReturnType")
@Composable
fun rememberAppState(scaffoldState:ScaffoldState=rememberScaffoldState(),
navController: NavHostController =rememberNavController(),
resources1: Resources = resources(),
coroutineScope: CoroutineScope=rememberCoroutineScope()
)=remember(scaffoldState,navController,resources1,coroutineScope){
OrderNowState(scaffoldState,navController,resources1,coroutineScope)
}
class OrderNowState(val scaffoldState:ScaffoldState,val navController:NavHostController,private val resources:Resources,coroutineScope:CoroutineScope)
@Composable
@ReadOnlyComposable
fun resources():Resources{
LocalConfiguration.current
return LocalContext.current.resources
}
然后,我們修改我們的OrderNowScreen視圖以包含之前定義的狀態(tài),如下所示:
@Preview
@Composable
fun OrderNowScreen(){
MyTestTheme {
Surface(modifier = Modifier.fillMaxSize(),color=MaterialTheme.colors.background) {
val appState =rememberAppState()
Scaffold(scaffoldState = appState.scaffoldState, topBar = { OrderNowTopBar()}, bottomBar = {OrderNowBottomBar()}){contentPadding->
println(contentPadding)
}
}
}
}
注意:
還需要向OrderNowScreen添加資源函數(shù),它將使用該函數(shù)訪問App資源。
突出顯示更改的代碼行如下:
Scaffold(
scaffoldState = appState.scaffoldState,
topBar = { OrderNowTopBar() },
bottomBar = { OrderNowBottomBar() }) { contentPadding ->
println(contentPadding)
}
有了上面的代碼,我們現(xiàn)在可以告訴Scaffold它應(yīng)該把哪個狀態(tài)作為引用:應(yīng)用程序的狀態(tài)。之后,視圖之間的導(dǎo)航操作、自發(fā)消息的呈現(xiàn),以及視圖只把AppState作為真實源的其他獨占任務(wù)都將被允許。
既然已經(jīng)為APP定義了狀態(tài),我們就可以繼續(xù)實現(xiàn)APP的導(dǎo)航了。
定義導(dǎo)航地圖
我們在應(yīng)用程序中使用的導(dǎo)航策略由以下元素組成:
NavHost:它是負(fù)責(zé)在視圖中顯示導(dǎo)航結(jié)果的組件。導(dǎo)航的結(jié)果由NavigationController和導(dǎo)航圖中給出的定義決定。
AppSoGraph:它是導(dǎo)航圖的實現(xiàn)。它應(yīng)該根據(jù)指定的路由將導(dǎo)航指向哪個視圖或可組合對象。
屏幕路由:它們是可以通過導(dǎo)航到達(dá)的應(yīng)用程序的不同屏幕。無論導(dǎo)航是從選項菜單、鏈接、按鈕還是任何其他活動代理激活的,都無關(guān)緊要。每個屏幕都有一個與之關(guān)聯(lián)的唯一路由。
一般導(dǎo)航圖
?我們將繼續(xù)在OrderNow中包含這些元素
OrderNowScreenRoute
首先,創(chuàng)建一個名為common -> navigation的新遍歷目錄。在這個包中,我們像這樣添加了一個名為OrderNowScreenRoute的類:
?在這個類中,可以導(dǎo)航到的屏幕定義如下:
sealed class OrderNowScreenRoute(val route:String){
object Home :OrderNowScreenRoute("home")
object Cart:OrderNowScreenRoute("cart")
object ProductList:OrderNowScreenRoute("product_list")
object ProductDetail:OrderNowScreenRoute("product_detail")
}
OrderNowNavHost 和 AppSoGraph
現(xiàn)在,我們創(chuàng)建OrderNowNavHost類,它將像這樣表示應(yīng)用程序的NavHost:
@Composable
fun OrderNowNavHost(appState:OrderNowState){
NavHost(navController = appState.navController,5,startDestination=1){
appSoGraph(appState)
}
}
fun NavGraphBuilder.appSoGraph(appState: OrderNowState) {
}
從之前的代碼片段中,我們應(yīng)該強調(diào)以下定義:
? OrderNowNavHost 需要知道 APP 的狀態(tài)。
? NavController 是從 APP 狀態(tài)中托管和獲取的。
? Navigation map(appSoGraph)將基于 APP 的狀態(tài)創(chuàng)建,并且是在 OrderNowNavHost 內(nèi)定義的擴展。
為了繼續(xù)完成 OrderNow 中導(dǎo)航的實現(xiàn),我們必須添加一個描述如下的輔助類。
NavigationBarSection
NavigationBarSection 是一個輔助類,代表應(yīng)用程序底部菜單中組成篩選組的部分。
請記住,我們可以從選項菜單或其他 UI 組件(如鏈接、按鈕或內(nèi)部重定向)中的操作開始導(dǎo)航。
在下一節(jié)中,我們將對內(nèi)部重定向(從按鈕、鏈接等)進(jìn)行更改;現(xiàn)在,讓我們專注于從 BottomBar 導(dǎo)航。
我們看到NavigationBarSection幫助器類將如何只對Home和Cart屏幕進(jìn)行分組,這是我們希望從BottomBar菜單啟用的選項。這個類將像這樣放置在導(dǎo)航目錄中:
它的實現(xiàn)是這樣的:
sealed class OrderNowScreenRoute(val route:String){
object Home :OrderNowScreenRoute("home")
object Cart:OrderNowScreenRoute("cart")
object ProductList:OrderNowScreenRoute("product_list")
object ProductDetail:OrderNowScreenRoute("product_detail")
}
sealed class NavigationBarSection(val title: String, val icon: ImageVector, val route:String){
companion object{
val sections = listOf(OrderNowScreenRoute.Home,OrderNowScreenRoute.Cart)
}
object Home:NavigationBarSection(title ="Home" , icon = Icons.Default.Home, route =OrderNowScreenRoute.Home.route)
object Cart:NavigationBarSection(title = "Cart", icon =Icons.Default.ShoppingCart, route = OrderNowScreenRoute.Cart.route)
}
將helper類添加到項目中后,我們繼續(xù)更新OrderNowNavHost類,如下所示:
@Composable
fun OrderNowNavHost(appState: OrderNowState, paddingValues: PaddingValues) {
NavHost(
navController = appState.navController,
startDestination = NavigationBarSection.Home.route,
modifier = Modifier.padding(paddingValues)
) {
appSoGraph(appState)
}
}
fun NavGraphBuilder.appSoGraph(appState: OrderNowState) {
composable(NavigationBarSection.Home.route) {
// HomeScreen()
}
composable(NavigationBarSection.Cart.route) {
// CartScreen()
}
composable(OrderNowScreenRoute.ProductList.route){
//ProductListScreen()
}
composable(OrderNowScreenRoute.ProductDetail.route){
//ProductDetailScreen()
}
}
appSoGraph函數(shù)的實現(xiàn)是NavGraphBuilder的擴展,在那里,我們?yōu)锳pp的每個屏幕指定導(dǎo)航地圖。另外,通過startDestination參數(shù),指定將首先呈現(xiàn)的默認(rèn)屏幕,即Home屏幕。采用更改的下一步是更新名為 OrderNowBottomBar 的類,如下所示:
@Composable
fun OrderNowBottomBar(navController: NavHostController) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
BottomNavigation(
backgroundColor = MaterialTheme.colors.background,
contentColor = contentColorFor(MaterialTheme.colors.background),
elevation = 10.dp
) {
NavigationBarSection.sections.forEach { section ->
val selected = currentDestination?.hierarchy?.any {
it.route == section.route
} == true
BottomNavigationItem(icon = {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_launcher_foreground),
contentDescription = stringResource(id = R.string.app_name)
)
},
label = { Text(text = stringResource(id =R.string.app_name)) },
selected = selected,
unselectedContentColor = Color.Gray,
selectedContentColor = Color.Red,
onClick = {
navController.navigate(section.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
})
}
}
}
對于添加到 NavigationBarSection 中的每個項目,BottomBar 中都會顯示一個選項。OrderNowBottomBar 的這個實現(xiàn)比之前的架構(gòu):主要結(jié)構(gòu)中的實現(xiàn)更清晰。
然后,我們再次更新 OrderNowScreen 視圖,如下所示:
@Preview
@Composable
fun OrderNowScreeen(){
MyTestTheme(){
Surface {
val appState =rememberAppState()
Scaffold(
scaffoldState = appState.scaffoldState,
topBar = {OrderNowTopBar()},
bottomBar = {OrderNowBottomBar(appState.navController)}
){contentPadding ->
OrderNowNavHost(appState,contentPadding)
}
}
}
}
現(xiàn)在 OrderNowBottomBar 將需要引用負(fù)責(zé)導(dǎo)航的 navController。
在Scaffold的內(nèi)容部分,添加了OrderNowNavHost的實例,該實例接收APP的一般狀態(tài)作為參數(shù)。Order Now with simple navigation:
從其他UI元素進(jìn)行導(dǎo)航
?現(xiàn)在我們已經(jīng)準(zhǔn)備好從 BottomBar 選項導(dǎo)航的實現(xiàn),我們需要定義從其他 UI 元素(例如按鈕、鏈接、DeepLinks)甚至根據(jù)應(yīng)用程序的其他內(nèi)部組件的請求以編程方式導(dǎo)航應(yīng)用程序。我們要做的第一個更改是添加一個名為 OrderNowNavigationState 的結(jié)構(gòu),它允許我們擴展應(yīng)用程序的一般狀態(tài)。
OrderNowNavigationState,是APP通用狀態(tài)的擴展,即一組用于導(dǎo)航目的的OrderNowState狀態(tài)的擴展。我們還將使用此結(jié)構(gòu)來集中取決于應(yīng)用程序狀態(tài)的導(dǎo)航邏輯。
OrderNowNavigationState的實現(xiàn)如下:
fun OrderNowState.popUp(){
navController.popBackStack()
}
fun OrderNowState.navigate(route:String){
navController.navigate(route){
launchSingleTop = true
}
}
fun OrderNowState.navigateAndPopUp(route:String,popUp:String){
navController.navigate(route){
launchSingleTop =true
popUpTo(popUp){
inclusive =true
}
}
}
fun OrderNowState.navigateSaved(route:String,popUp:String){
navController.navigate(route){
launchSingleTop =true
restoreState =true
popUpTo(popUp){
saveState =true
}
}
}
fun OrderNowState.clearAndNavigate(route:String){
navController.navigate(route){
launchSingleTop =true
popUpTo(0){ inclusive =true}
}
}
對Home、ProductList和ProductDetail屏幕做了一些更改,如下圖所示:
?以in - home視圖為例,導(dǎo)航動作通過Button執(zhí)行,方式如下:
@Composable
fun HomeScreen(goToProductList:()->Unit,modifier:Modifier=Modifier,viewModel2:HomeViewModel =hiltViewModel()){
Column(){
Button(onClick = goToProductList,){
Text(text = stringResource(id = R.string.app_name))
}
}
}
代碼的重要部分是“Go to —> ProductList Screen”按鈕的動作 onClick = goToProductList 的定義,其中通過設(shè)計原理章節(jié)中解釋的狀態(tài)提升技術(shù),我們將動作 goToProductList 委托給在 OrderNowNavHost 中定義的 appSoGraph 導(dǎo)航圖如下:
un NavGraphBuilder.appSoGraph1(appState:OrderNowState){
val goToListFromHome:()->Unit ={
appState.navigateSaved(OrderNowScreenRoute.ProductList.route,OrderNowScreenRoute.Home.route)
}
composable(NavigationBarSection.Home.route){
HomeScreen(goToProductList =goToListFromHome)
}
}
回想一下,navigatesave函數(shù)是OrderNowNavigationState結(jié)構(gòu)中定義的擴展的一部分。同樣的實現(xiàn)應(yīng)用于導(dǎo)航到其他ProductList和ProductDetail屏幕,這樣在OrderNowNavHost中的性能如下所示:
fun NavGraphBuilder.appSoGraph2(appState:OrderNowState){
val homeRoute= OrderNowScreenRoute.Home.route
val listRoute= OrderNowScreenRoute.ProductList.route
val detailRoute =OrderNowScreenRoute.ProductDetail.route
val goToListFromHome:()->Unit ={
appState.navigateSaved(listRoute,homeRoute)
}
val goToDetailFromList:()->Unit ={
appState.navigateSaved(detailRoute,listRoute)
}
val goBack:()->Unit ={
appState.popUp()
}
composable(NavigationBarSection.Home.route){
HomeScreen(goToProductList =goToListFromHome)
}
composable(NavigationBarSection.ProductList.route){
ProductListScreen(goToProductDetail =goToDetailFromList,goBack =goBack)
}
composable(NavigationBarSection.ProductDetail.route){
ProductDetailScreen(goBack =goBack)
}
}
總結(jié)起來,前面的代碼段中配置了以下導(dǎo)航定義(導(dǎo)航圖):
- 從主頁(Home screen)導(dǎo)航到產(chǎn)品列表(ProductList screen)。
- 從產(chǎn)品列表(ProductList screen)導(dǎo)航到產(chǎn)品詳情(ProductDetail screen)。
- 從產(chǎn)品詳情(ProductDetail screen)導(dǎo)航回到上一個呈現(xiàn)的屏幕。
- 從底部導(dǎo)航欄菜單導(dǎo)航到主頁(Home screen)。
- 從底部導(dǎo)航欄菜單導(dǎo)航到購物車(Cart screen)。
到此為止,我們在 OrderNow 應(yīng)用中已經(jīng)實現(xiàn)了一個很好的基本導(dǎo)航。然而,還有一些東西缺失。 您有什么想法可能是缺失的嗎? 我們需要在導(dǎo)航中包含數(shù)據(jù)或信息的傳遞。 由于在本章中設(shè)計了相關(guān)的實現(xiàn),因此很容易實現(xiàn)數(shù)據(jù)傳遞。我們將在最后“實現(xiàn)功能”中詳細(xì)探討。文章來源:http://www.zghlxwxcb.cn/news/detail-632368.html
總結(jié)
本章中,我們完成了應(yīng)用程序的主要部分的設(shè)計,這些將是以后添加功能的基礎(chǔ)。 我們知道導(dǎo)航是應(yīng)用程序的重要組成部分,必須從應(yīng)用程序設(shè)計的開始就考慮它。 在本章中,我們采用了谷歌的建議,將狀態(tài)納入導(dǎo)航邏輯中。 同時,讀者可以注意到我們的策略中沒有直接涉及 ViewModel 或 View,這使得它在測試時更加靈活,可以檢查導(dǎo)航。文章來源地址http://www.zghlxwxcb.cn/news/detail-632368.html
到了這里,關(guān)于為Android構(gòu)建現(xiàn)代應(yīng)用——應(yīng)用導(dǎo)航設(shè)計的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!