iT邦幫忙

2022 iThome 鐵人賽

DAY 27
0

前言


今天來做 Training 頁面

預覽


先擺上今天的成果

預設主題 黑暗主題
https://ithelp.ithome.com.tw/upload/images/20221003/20136048nbxtWBrerf.png https://ithelp.ithome.com.tw/upload/images/20221003/20136048KfZFdPIPfO.png
@Preview
@Composable
fun PrevTrainingScreen() {
    KTheme(darkTheme = true) { //darkTheme true & false
        TrainingScreen()
    }
}
  • darkTheme 可以設定預覽主題效果

Scaffold


Scaffold 來佈局,上有 topBar 下有 floatingActionButton

Scaffold(
    topBar = {
        CenterAlignedTopAppBar(
            title = { /* 日期 */ },
            navigationIcon = { Icon(Icons.Filled.ArrowBack, contentDescription = "Back") }
        )
    },
    floatingActionButton = {
        FloatingActionButton(onClick = {/* 新增訓練或休息 */ }) {
            Icon(Icons.Filled.Add, contentDescription = "Add Training")
        }
    }
) { padding ->
		//訓練列表
}
  • topBar:這次使用 CenterAlignedTopAppBar 可以讓標題置中,title 放入日期並且有 formatter 過,navigationIcon 用 material icon 的 ArrowBack
  • floatingActionButton :和主頁面同樣式,onClick 需要處理新增的選單是要新增訓練還是休。

LazyColumn


Scaffold(
	//...
) { padding ->
    LazyColumn(
        modifier = Modifier
            .padding(padding)
            .padding(horizontal = 8.dp)
    ) {
        items(trainingList) {
            TrainingItem(it, onClick = {/*TODO*/ })
        }
    }
}
  • LazyColumnmodifier 先將Scaffoldpadding 加入,再設定左右邊距離為 8.dp
  • items :帶入資料trainingList ,把一組訓練的 Composable 提成TrainingItem

TrainingItem


這是放置一組訓練的資訊卡。 需要監聽點擊功能,所以在

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TrainingItem(
    trainingItem: TrainingItem,
    onClick: () -> Unit
) {
    Card(
        onClick = onClick,
        modifier = Modifier.fillMaxWidth().padding(bottom = 4.dp),
        colors = CardDefaults.cardColors(
            containerColor = if (trainingItem.isRest) {
                MaterialTheme.colorScheme.tertiaryContainer
            } else {
                MaterialTheme.colorScheme.primaryContainer
            }
        ),
        shape = RoundedCornerShape(15)
    ) {
        
    }
}
  • onClick:監聽點擊事件,由TrainingItem 傳入的方法作為參數,因為 onClick 的卡片還是實驗性質的,所以要加上 @OptIn(ExperimentalMaterial3Api::class)
  • modifier:先將卡片範圍展到最寬,再設置和下方卡片距離4.dp
  • colors:複製Card預設顏色參數,更改背景色,為了能響應黑色模式,採用MaterialTheme.colorScheme 的顏色
  • shape:想要圓角效果可以使用 RoundedCornerShapeCircleShape 其實就是 RoundedCornerShape(50)
Card(
	//...
) {
	Row(
	    modifier = Modifier.padding(vertical = 16.dp, horizontal = 16.dp),
	    horizontalArrangement = Arrangement.SpaceBetween,
	) { //... }    
}

  • 欄位內容我用 Row 來呈現
  • modifier :撐開上下 vertical = 16.dp左右 horizontal = 16.dp
  • horizontalArrangement :採用Arrangement.SpaceBetween 想要呈現平均分配的樣式。
Row(
     //...
) { 
		if (trainingItem.isRest) {
		
		} else {
		
		}
}  

  • Row的內容分為兩種,一個是休息,一個是訓練
 if (trainingItem.isRest) {
    Box(modifier = Modifier.weight(3f))
    Text(
        text = "休息",
        modifier = Modifier.weight(1f),
        textAlign = TextAlign.Right,
        color =  MaterialTheme.colorScheme.onTertiaryContainer
    )
    Text(
        text = "${trainingItem.restSecond} s",
        modifier = Modifier.weight(1f),
        textAlign = TextAlign.Right,
        color =  MaterialTheme.colorScheme.onTertiaryContainer
    )
} else {
    Text("${trainingItem.title}", modifier = Modifier.weight(1f))
    Text(
        text = "${trainingItem.weight} ${trainingItem.weightUnit.name} ",
        modifier = Modifier.weight(1f)
    )
    Text(
        text = "${trainingItem.times} ${trainingItem.timesUnit.name} ",
        modifier = Modifier.weight(1f)
    )
    Box(modifier = Modifier.weight(2f))
}
  • 為了呈現均分的樣式 ,使用 Modifier.weight(3f) 來分配比例。
  • textAlign = TextAlign.Right 為了讓 Text 看起來佔滿版面,並且與訓練內容做區分,靠右側對齊。
  • 文字顏色設定成 MaterialTheme.colorScheme.onTertiaryContainer ,對應Container 這裡用了 onContainer

全部的程式碼

@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TrainingScreen() {
    var date = LocalDateTime.now()
		//假資料
    var trainingList = listOf<TrainingItem>(
        TrainingItem(
            index = 1,
            title = "深蹲",
            weight = 30,
            times = 15,
        ),
        TrainingItem(
            index = 2,
            title = "深蹲",
            weight = 30,
            times = 15,
        ),
        TrainingItem(
            index = 3,
            title = "深蹲",
            weight = 30,
            times = 15,
        ),
        TrainingItem(
            index = 4,
            isRest = true,
            restSecond = 30
        ),
    )

    Scaffold(
        topBar = {
            CenterAlignedTopAppBar(
                title = {
                    Text(
                        date.format(formatter),
                        modifier = Modifier.clickable { /*TODO*/ })
                },
                navigationIcon = { Icon(Icons.Filled.ArrowBack, contentDescription = "Back") }
            )
        },
        floatingActionButton = {
            FloatingActionButton(onClick = {/*TODO*/ }) {
                Icon(Icons.Filled.Add, contentDescription = "Add Training")
            }
        }
    ) { padding ->
        LazyColumn(
            modifier = Modifier
                .padding(padding)
                .padding(horizontal = 8.dp)
        ) {
            items(trainingList) {
                TrainingItem(it, onClick = {/*TODO*/ })
            }
        }
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TrainingItem(
    trainingItem: TrainingItem,
    onClick: () -> Unit
) {
    Card(
        onClick = onClick,
        modifier = Modifier.fillMaxWidth().padding(bottom = 4.dp),
        colors = CardDefaults.cardColors(
            containerColor = if (trainingItem.isRest) {
                MaterialTheme.colorScheme.tertiaryContainer
            } else {
                MaterialTheme.colorScheme.primaryContainer
            }
        ),
        shape = RoundedCornerShape(15)
    ) {
        Row(
            modifier = Modifier.padding(vertical = 16.dp, horizontal = 16.dp),
            horizontalArrangement = Arrangement.SpaceBetween,
        ) {
            if (trainingItem.isRest) {
                Box(modifier = Modifier.weight(3f))
                Text(
                    text = "休息",
                    modifier = Modifier.weight(1f),
                    textAlign = TextAlign.Right,
                    color =  MaterialTheme.colorScheme.onTertiaryContainer
                )
                Text(
                    text = "${trainingItem.restSecond} s",
                    modifier = Modifier.weight(1f),
                    textAlign = TextAlign.Right,
                    color =  MaterialTheme.colorScheme.onTertiaryContainer
                )
            } else {
                Text("${trainingItem.title}", modifier = Modifier.weight(1f))
                Text(
                    text = "${trainingItem.weight} ${trainingItem.weightUnit.name} ",
                    modifier = Modifier.weight(1f)
                )
                Text(
                    text = "${trainingItem.times} ${trainingItem.timesUnit.name} ",
                    modifier = Modifier.weight(1f)
                )
                Box(modifier = Modifier.weight(2f))
            }
        }
    }
}

今日訓練
拳擊 50分鐘


上一篇
Day 26 把主畫面組起來!
下一篇
Day28 實作 DateTimePicker
系列文
今年一定減成功!Jetpack Compose 做出重訓紀錄APP30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言