學以致用,把主畫面組裝起來。
這裡傳三個 funtion 用來做頁面轉換用。
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen(
onSearchBarClick: () -> Unit,
onAddTrainingClick: () -> Unit,
onTrainingClick: (Long) -> Unit,
) {
}
onSearchBarClick 搜尋欄被點擊時,要跳轉頁面。onAddTrainingClick 新增事件時,要跳轉頁面。onTrainingClick 當事件被點擊時,要跳轉頁面,並且戴上 Training 的 id 值利用 Scaffold 來佈局畫面。
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen(
//...
) {
Scaffold(
topBar = {//... },
floatingActionButton = {//...}
) { padding -> //... }
}
topBar 放搜尋欄floatingActionButton 新增事件的懸浮按鈕padding 自動算好扣除 topBar 的 padding 距離。帶入最外層的 composable modifier.padding 就可以用了搜尋欄位
TopAppBar(
modifier = Modifier
.clickable { onSearchBarClick() }
.padding(8.dp),
title = {
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = "",
onValueChange = {},
placeholder = {
Text(text = "搜尋")
},
shape = CircleShape
)
}
)
modifier 設置了clickable 點擊就跳轉頁面,padding上下左右外距8.dp
OutlinedTextField 呈現placeholder提示字串,並且將外型shape改為原型。因為要跳轉頁面 value 值不特別紀錄,onValueChange也不處理。新增訓練的懸浮按鈕
import androidx.compose.material.icons.filled.Add
floatingActionButton = {
FloatingActionButton(onClick = { onAddTrainingClick() }) {
Icon(Icons.Filled.Add, contentDescription = "Add Training")
}
}
採用 FloatingActionButton ,onClick lambda 放入 onAddTrainingClick() 來跳轉頁面。Button content 使用 material icon filled 樣式的 add 。
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen(
//...
) {
Scaffold(
//...
) { padding ->
//1.check data is not empty
if (trainingList.isEmpty()) {
Text(modifier = Modifier.fillMaxSize(), text = "來安排訓練菜單吧!")
} else {
LazyVerticalGrid(//...) { //2.
items(trainingList) { training ->
//...
}
}
}
}
}
LazyVerticalGrid 在 items 傳入 trainingList,lambda 就能使用 training 資料。{ padding ->
LazyVerticalGrid(
modifier = Modifier
.padding(padding)//1.
.padding(horizontal = 16.dp),
columns = GridCells.Fixed(2),//2.
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
//...
}
}
LazyVerticalGrid 是最上層的 composable 帶入 Scaffold 的 padding 在 modifier 可以扣除 topAppBar 的高。columns 設置欄位數為 2horizontalArrangement 和 verticalArrangement 用 spacedBy調整 item 和 item 之間的距離為 8.dp
TrainingCard 負責呈現一個訓練的卡片資訊
items(trainingList) { training ->
TrainingCard(
onCardClick = { id ->
onTrainingClick(id) //1.
},
training = training
)
}
onTrainingClick 可以轉跳頁面並且帶上 training id
TrainingCard 運用了 Card 、Text 元件還有 Column 來佈局。
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TrainingCard(
onCardClick: (Long) -> Unit,
training: Training
) {
Card(
onClick = {
onCardClick(training.id)
},
) {
Column(
modifier = Modifier.padding(8.dp)
) {
Card(
shape = CircleShape,
colors = CardDefaults.cardColors(containerColor = Color.Gray),
modifier = Modifier.fillMaxWidth()
) {
Text(
text = training.date.format(formatter),
style = MaterialTheme.typography.titleMedium,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(4.dp),
color = Color.White
)
}
Text(
text = training.trainingContext,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(8.dp)
)
}
}
}
指定日期樣式
val formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd (E)", Locale.TAIWAN)
今天將 main 頁面調整完,還剩下在 navigation 的資料處理,以及將 training list 從 Data 層串到 ViewModel。以及 GridLayout 在左右兩側高度不同時沒有自動適應高度。
今日運動
休息