如何是Jetpack Compose構(gòu)建漂亮的應(yīng)用程序
Jetpack compose 是在 Android 上構(gòu)建 UI 的未來(lái)。
如果您完全不熟悉 android 并且不知道 Jetpack Compose 是什么——它基本上是一種構(gòu)建本機(jī)用戶界面的新方法。
Jetpack compose官方站點(diǎn)
https://developer.android.com/jetpack/compose
在本文中,您將了解如何使用 Jetpack Compose 遵循最佳實(shí)踐進(jìn)行 UI 開(kāi)發(fā)。
我們正在建設(shè)什么
我們將構(gòu)建一個(gè)顯示 Apple Music 專輯列表的應(yīng)用程序。讓我們調(diào)用應(yīng)用程序MyMusic。
從我的github 存儲(chǔ)庫(kù)中,您將學(xué)習(xí)如何:
https://github.com/ibrajix/MyMusic文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-436638.html
- 使用推薦的方法(啟動(dòng) API)構(gòu)建啟動(dòng)畫(huà)面
https://developer.android.com/guide/topics/ui/splash-screen
- 使用各種 UI 可組合項(xiàng),例如行、列、惰性列、動(dòng)畫(huà) API
- 將 MVVM 模式與 Jetpack Compose 結(jié)合使用(使用可觀察對(duì)象和狀態(tài)持有者,如 StateFlow)
- 使用Room 數(shù)據(jù)庫(kù)從 JSON 文件保存本地?cái)?shù)據(jù)
- 在應(yīng)用程序上實(shí)現(xiàn)搜索功能
- 使用這個(gè)很棒的庫(kù)實(shí)現(xiàn)帶有過(guò)渡動(dòng)畫(huà)的簡(jiǎn)單導(dǎo)航
https://github.com/raamcosta/compose-destinations
- 如何使用這個(gè)很棒的庫(kù)有效地顯示 gif 等圖像
https://github.com/skydoves/Landscapist
為了降低復(fù)雜性,本文重點(diǎn)介紹 UI。
SpashScreen
啟動(dòng)畫(huà)面是在您的應(yīng)用程序內(nèi)容加載之前顯示的內(nèi)容。這是向用戶展示您的品牌形象或徽標(biāo)的一種方式。
可悲的是,沒(méi)有辦法專門(mén)用 compose 來(lái)實(shí)現(xiàn)當(dāng)前的 splash API。我們?nèi)匀恍枰恍?xml 代碼
- 在
values/themes.xml
下。添加啟動(dòng)畫(huà)面主題
代碼中注釋了每個(gè)屬性的作用
<!--Parent Theme-->
<style name="Theme.MyMusic" parent="android:Theme.Material.Light.NoActionBar">
......
</style>
<!--Splash Screen Theme-->
<style name="Theme.MyMusic.SplashScreen" parent="Theme.SplashScreen">
<!--splash screen background-->
<item name="windowSplashScreenBackground">@color/splash_screen_background_color</item>
<!--splash screen drawable-->
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_launcher_foreground</item>
<!--post splash screen - displayed after splash screen-->
<item name="postSplashScreenTheme">@style/Theme.MyMusic</item>
</style>
- 確保將主題包含在AndroidManifest.xml root 標(biāo)記中
android:theme="@style/Theme.MyMusic.SplashScreen"
在您的 Launcher Activity 中安裝主題,它應(yīng)該是您的MainActivity.kt
class MainActivity : ComponentActivity()
.........
super.onCreate(savedInstanceState)
......
installSplashScreen()
- 運(yùn)行該應(yīng)用程序,您的初始屏幕應(yīng)該可以正常工作。
StartSceen
創(chuàng)建一個(gè)新的composable,命名為StartScreen(代碼路徑 ui/screens/start/StartScreen.kt
)
@RootNavGraph(start = true)
@Destination(style = StartScreenTransitionAnimation::class)
@Composable
fun StartScreen(
modifier: Modifier = Modifier,
navigator: DestinationsNavigator
) {
..//we'll build the layout above here
}
@RootNavGraph
- 這是我們導(dǎo)航庫(kù)中的一個(gè)注釋,表示這是我們導(dǎo)航圖的起始屏幕。
@Destination
- 也是來(lái)自我們導(dǎo)航庫(kù)的注釋,表示這個(gè)組合式是一個(gè)可以讓用戶往返導(dǎo)航的目的地。我們還包括了一個(gè)過(guò)渡動(dòng)畫(huà)的樣式屬性(請(qǐng)查看animations/StartScreenTransition.kt
)。
@Modifier
- 是傳遞給組合式的參數(shù),用于裝飾組合式(例如,大小、背景等)。
@Navigator
- 幫助我們從一個(gè)目的地或屏幕導(dǎo)航到另一個(gè)。
- 正如您從上面的圖2中可以看到的那樣,卡片被放置在肯伊·威斯特(Kanye West)的圖像上方。
- 因此,我們將使用一個(gè)Box。
- Box是一個(gè)UI可組合,可以讓您將項(xiàng)目放置在其他項(xiàng)目之上。
請(qǐng)?jiān)谏厦娴膡…}之間放置以下可組合:
Box(
modifier = modifier
.fillMaxSize()
){
.......
}
- modifier 說(shuō)明該 Box 應(yīng)填充整個(gè)屏幕的大小。
- 現(xiàn)在,在 Box 內(nèi)部,我們放置我們的卡尼·韋斯特圖片,這是一個(gè) GIF(使用我們的圖像加載庫(kù))。
Box(
modifier = modifier
.fillMaxSize()
){
GlideImage(
imageModel = R.drawable.kanye
)
........
}
- 接下來(lái),我們需要?jiǎng)?chuàng)建一個(gè)放置在 Kanye 圖片上方的卡片。我們可以使用 Card 組合來(lái)實(shí)現(xiàn)。
Box(
modifier = modifier
.fillMaxSize()
){
GlideImage(
imageModel = R.drawable.kanye
)
Card(modifier = modifier
.fillMaxWidth(0.8F)
.align(Alignment.BottomCenter)
.padding(bottom = 50.dp),
shape = RoundedCornerShape(50.dp),
backgroundColor = MaterialTheme.colors.secondary
) {
...........
}
}
- 我們指定卡片應(yīng)該在屏幕底部居中,寬度恰好占據(jù)屏幕的 80%,并有 50dp 的 padding。
- 我們還指定了卡片應(yīng)該有圓角,半徑為 50dp,并具有輔助背景色。
- 從上圖2中可以看出,紅色箭頭表示物品是從上到下(縱向)排列的。
- Column是一個(gè)UI組件,可將其子項(xiàng)垂直排列在一起。
Box(
modifier = modifier
.fillMaxSize()
){
GlideImage(
imageModel = R.drawable.kanye
)
Card(modifier = modifier
.fillMaxWidth(0.8F)
.align(Alignment.BottomCenter)
.padding(bottom = 50.dp),
shape = RoundedCornerShape(50.dp),
backgroundColor = MaterialTheme.colors.secondary
) {
Column(
modifier = modifier
.padding(bottom = 20.dp),
verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally
) {
...........
}
}
}
- 這些修飾符和屬性都是很容易理解的。
- 我們確保在此列中放置的所有項(xiàng)目從上到下(垂直)均勻分布。
- 同時(shí),確保項(xiàng)目從左到右(水平)居中。
- 現(xiàn)在,我們將在列中添加其他 UI 組合件——文本和按鈕。
開(kāi)始界面的完整代碼如下:
//StartScreen.kt
@RootNavGraph(start = true)
@Destination(style = StartScreenTransitionAnimation::class)
@Composable
fun StartScreen(
modifier: Modifier = Modifier,
navigator: DestinationsNavigator
) {
Box(
modifier = modifier
.fillMaxSize()
){
GlideImage(
imageModel = R.drawable.kanye
)
Card(modifier = modifier
.fillMaxWidth(0.8F)
.align(Alignment.BottomCenter)
.padding(bottom = 50.dp),
shape = RoundedCornerShape(50.dp),
backgroundColor = MaterialTheme.colors.secondary
) {
Column(
modifier = modifier
.padding(bottom = 20.dp),
verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
modifier = modifier
.padding(30.dp),
text = stringResource(id = R.string.explore_your_world_of_music),
style = MaterialTheme.typography.h1,
textAlign = TextAlign.Center,
color = MaterialTheme.colors.onSecondary
)
Text(
modifier = modifier
.padding(horizontal = 30.dp),
text = stringResource(id = R.string.see_trending_songs_from_favs),
style = MaterialTheme.typography.caption,
textAlign = TextAlign.Center,
fontSize = 14.sp,
color = MaterialTheme.colors.onSecondary
)
Button(
modifier = modifier
.fillMaxWidth(0.6f)
.height(80.dp)
.padding(top = 8.dp)
.align(Alignment.CenterHorizontally)
.padding(8.dp),
shape = RoundedCornerShape(50.dp),
onClick = {
navigator.popBackStack()
navigator.navigate(HomeScreenDestination)
}
) {
Text(
text = stringResource(id =R.string.get_started),
style = MaterialTheme.typography.h3
)
}
}
}
}
}
- 請(qǐng)注意,我們?cè)诎粹o點(diǎn)擊時(shí)使用
onClick
lambda 參數(shù)導(dǎo)航到HomeScreenDestination
。 - 請(qǐng)確保您已經(jīng)創(chuàng)建了
HomeScreen
composable,請(qǐng)檢查ui/screens/home/HomeScreen
。
它應(yīng)該帶有@Destination
注解。在成功構(gòu)建后,這將自動(dòng)為您創(chuàng)建HomeScreenDestination
文件。
Home Screen
- 您必須已經(jīng)創(chuàng)建了HomeScreen可組合項(xiàng)(檢查
ui/screens/home/HomeScreen.kt
) - 主屏幕基本上由從上到下排列的項(xiàng)目組成——垂直(如上面的黑色箭頭所示)
@Destination(style = StartScreenTransitionAnimation::class)
@Composable
fun HomeScreen(
navigator: DestinationsNavigator,
albumDatabaseViewModel: AlbumDatabaseViewModel = hiltViewModel()
){
//--> Home screen layout here
}
個(gè)人而言,我喜歡干凈、可重復(fù)使用的代碼。所以,我不會(huì)將整個(gè)主屏幕項(xiàng)目放在此組合中。我們將創(chuàng)建另一個(gè)名為 HomeScreenItems.kt 的組合,并將其放在我們的 HomeScreen 中。
//HomeScreen.kt
@Destination(style = StartScreenTransitionAnimation::class)
@Composable
fun HomeScreen(
navigator: DestinationsNavigator,
albumDatabaseViewModel: AlbumDatabaseViewModel = hiltViewModel()
){
.............// Check full code for other variables
HomeScreenItems(navigator = navigator, albums = albums.value,
onCardClicked = {
shouldOpenAlbumDetails = true
albumUrl = it
},
onPopularAlbumClicked = {
shouldOpenTrendingAlbums = true
}
)
}
這是我們的HomeScreenItems.kt
文件
//HomeScreenItems.kt
@Composable
fun HomeScreenItems(
modifier: Modifier = Modifier,
navigator: DestinationsNavigator,
albums: List<Album>,
albumDatabaseViewModel: AlbumDatabaseViewModel = hiltViewModel(),
onCardClicked: (String) -> Unit,
onPopularAlbumClicked: () -> Unit
) {
........
}
-
該可組合接受許多參數(shù)。
我將強(qiáng)調(diào)一下之前沒(méi)有解釋過(guò)的參數(shù): -
albums
- 來(lái)自Room數(shù)據(jù)庫(kù)實(shí)體/表的專輯列表 -
albumDatabaseViewModel
- 數(shù)據(jù)源的viewModel -
onCardClicked
- 當(dāng)卡片被點(diǎn)擊時(shí)調(diào)用的Lambda函數(shù) -
onPopularAlbumClicked
- 當(dāng)點(diǎn)擊熱門(mén)專輯時(shí)調(diào)用的Lambda函數(shù) -
由于我們希望我們的主屏幕可以滾動(dòng),并且隨著我們滾動(dòng)逐漸加載項(xiàng)目,因此我們將使用LazyColumn作為根布局。
-
LazyColumn是一個(gè)垂直滾動(dòng)的可組合列表,僅組合和布局當(dāng)前可見(jiàn)的項(xiàng)目。
//HomeScreenItems.kt
@Composable
fun HomeScreenItems(
modifier: Modifier = Modifier,
navigator: DestinationsNavigator,
albums: List<Album>,
albumDatabaseViewModel: AlbumDatabaseViewModel = hiltViewModel(),
onCardClicked: (String) -> Unit,
onPopularAlbumClicked: () -> Unit
) {
LazyColumn(
modifier = modifier
.fillMaxSize()
.background(MaterialTheme.colors.bgHome)
.padding(20.dp)
){
.........
}
}
- 因此,我們想在 LazyColumn 中放置什么?非動(dòng)態(tài)內(nèi)容(單個(gè)靜態(tài)項(xiàng))和動(dòng)態(tài)內(nèi)容(變化的項(xiàng))。
- 從圖3中,您會(huì)發(fā)現(xiàn)藍(lán)色框和綠色框中的項(xiàng)目是非動(dòng)態(tài)內(nèi)容。它們不會(huì)改變。
- 因此,我們將使用單個(gè)項(xiàng)目 lambda 函數(shù)來(lái)顯示這些項(xiàng)。
- 每個(gè)部分都有一個(gè)用于顯示
UserHomeSection()
、SearchSection()
和PopularAlbumSection()
的組合體。
//HomeScreenItems.kt
LazyColumn(
modifier = modifier
.fillMaxSize()
.background(MaterialTheme.colors.bgHome)
.padding(20.dp)
){
/**
* Non-Dynamic Items
*/
item {
//first section
UserHomeSection()
//search home screen
SearchSection(
searchTextFieldValue = "",
onSearchTextFieldValueChange = { },
onSearchTextFieldClicked = { navigator.navigate(SearchScreenDestination) },
searchFieldPlaceHolder = R.string.search_albums,
searchEnabled = false,
showKeyboardOnStart = false
)
//popular item section
PopularAlbumSection(
cardTextTitle = R.string.popular,
cardTextItem = R.string.top_trending_albums,
cardImage = R.drawable.ic_character,
onPopularAlbumCardClicked = {
//popular album clicked, go to apple music
onPopularAlbumClicked()
}
)
}
item{
Text(
modifier = modifier
.fillMaxWidth()
.padding(top = 12.dp),
style = MaterialTheme.typography.h2,
fontSize = 18.sp,
color = MaterialTheme.colors.onSecondary,
text = stringResource(id = R.string.all_albums)
)
}
.................
}
}
- 這樣做是為了分離關(guān)注點(diǎn),并且主要是為了可重用性(例如,我可以在應(yīng)用程序的其他位置使用SearchSection可組合部件,而無(wú)需復(fù)制SearchSection可組合部件中的整個(gè)代碼)
- 實(shí)際上,我在SearchScreen中使用了相同的可組合部件。
- 請(qǐng)注意,在圖3中,第一個(gè)框標(biāo)記為藍(lán)色。這只是表示項(xiàng)目從左到右(水平)放置。因此,我們需要使用名為Row的可組合部件。
- Row用于在屏幕上水平放置項(xiàng)目。
- 現(xiàn)在,對(duì)于我們從Room數(shù)據(jù)庫(kù)獲取的動(dòng)態(tài)項(xiàng)目,我們將使用稱為items的lambda函數(shù)來(lái)顯示它們。
我們完整的HomeScreenItems.kt
如下:
//HomeScreenItems.kt
@Composable
fun HomeScreenItems(
modifier: Modifier = Modifier,
navigator: DestinationsNavigator,
albums: List<Album>,
albumDatabaseViewModel: AlbumDatabaseViewModel = hiltViewModel(),
onCardClicked: (String) -> Unit,
onPopularAlbumClicked: () -> Unit
) {
LazyColumn(
modifier = modifier
.fillMaxSize()
.background(MaterialTheme.colors.bgHome)
.padding(20.dp)
){
/**
* Non-Dynamic Items
*/
item {
//first section
UserHomeSection()
//search home screen
SearchSection(
searchTextFieldValue = "",
onSearchTextFieldValueChange = { },
onSearchTextFieldClicked = { navigator.navigate(SearchScreenDestination) },
searchFieldPlaceHolder = R.string.search_albums,
searchEnabled = false,
showKeyboardOnStart = false
)
//popular item section
PopularAlbumSection(
cardTextTitle = R.string.popular,
cardTextItem = R.string.top_trending_albums,
cardImage = R.drawable.ic_character,
onPopularAlbumCardClicked = {
//popular album clicked, go to apple music
onPopularAlbumClicked()
}
)
}
/**
* Dynamic Items
*/
item{
Text(
modifier = modifier
.fillMaxWidth()
.padding(top = 12.dp),
style = MaterialTheme.typography.h2,
fontSize = 18.sp,
color = MaterialTheme.colors.onSecondary,
text = stringResource(id = R.string.all_albums)
)
}
items(items = albums){ album->
AlbumCard(
album = album,
onClickCard = { albumUrl->
//card clicked, go to details screen
onCardClicked(albumUrl)
},
onClickLike = { isLiked, albumId->
albumDatabaseViewModel.doUpdateAlbumLikedStatus(!isLiked, albumId)
}
)
}
}
}
我創(chuàng)建了一個(gè)名為AlbumCard的可重復(fù)使用的可組合項(xiàng),我們可以將其用作顯示所有動(dòng)態(tài)項(xiàng)目的模型。
確保檢查完整代碼以正確理解其工作原理。
我希望我已經(jīng)能夠解釋基本 UI 如何與 Jetpack Compose 一起工作。有關(guān) UI 的更多信息,請(qǐng)查看官方文檔
github代碼網(wǎng)址
https://github.com/ibrajix/MyMusic
參考
https://ibrajix.medium.com/how-i-built-this-nice-looking-app-using-jetpack-compose-3974db7eb9e文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-436638.html
到了這里,關(guān)于如何是Jetpack Compose構(gòu)建漂亮的應(yīng)用程序的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!