iT邦幫忙

2025 iThome 鐵人賽

DAY 11
0

這裡就是第一部分的完結了,現在要透過將App的介面做出來來熟悉與複習
雖然說我們還沒有學到如何切換頁面與跨頁面保留資料,但是這不妨礙我們先把畫面給做出來,之後再結合起來

代辦清單

這部分會用到很多的container,還有基本的UI元素
首先建一個資料夾,我這邊是把清單和清單組瀏覽分開,讓項目結構更清晰:

清單組

在day3時候,我們的清單是分組的,所以這邊也這麼分

data class TodoItem(var title: String, var content: String, var group: String, var done: Boolean = false)  
  
val todoItems = listOf<TodoItem>(  
    TodoItem("a", "content", "a list"),  
    TodoItem("b", "empty", "a list", true),  
    TodoItem("c", "nothing", "b list", true),  
    TodoItem("d", "void", "b list"),  
    TodoItem("e", "vacant", "a list"),  
)

先建立一個清單用作測試

首先,我們明顯需要一個LazyColumn

@Composable  
fun TodoGroups() {  
    val groupList = todoItems.groupBy {  
        it.group  
    }.toList()  
  
    LazyColumn(  
        modifier = Modifier.fillMaxSize(),  
        horizontalAlignment = Alignment.CenterHorizontally,  
        contentPadding = PaddingValues(10.dp, 8.dp),  
        verticalArrangement = Arrangement.spacedBy(5.dp)  
    ) {  
        items(groupList) { group->  
            GroupItem(group)  
        }  
    }
}

在用group將零散的todoItems組合起來後,就可以準確地對groupList展開了
但是我們還需要一個可以展示Group的GroupItem

@Composable  
fun GroupItem(group: Pair<String, List<TodoItem>>) {  
    val undoneAmount = group.second.count { !it.done }  
    val totalAmount = group.second.size  
    Column(  
        modifier = Modifier.fillMaxWidth()  
            .background(Color.Gray, RoundedCornerShape(5.dp)),  
    ) {  
        Text(  
            group.first,  
            fontWeight = FontWeight.Bold,  
            fontSize = 20.sp,  
            modifier = Modifier.padding(10.dp, 3.dp)  
        )  
        Text(  
            "$undoneAmount/$totalAmount tasks undone",  
            fontSize = 14.sp,  
            modifier = Modifier.padding(10.dp, 3.dp)  
        )  
    }  
}

根據觀察,它是column與文字的組合而已
完成後預覽一下,雖然顏色不對,但配置沒問題

清單組瀏覽

這邊一樣,用LazyColumn套Item

@Composable  
fun TodoDisplay(groupTodos: List<TodoItem>) {  
    LazyColumn(  
        modifier = Modifier.fillMaxSize(),  
        horizontalAlignment = Alignment.CenterHorizontally,  
        verticalArrangement = Arrangement.spacedBy(6.dp)  
    ) {  
        items(todoItems) {  
            TodoItemBox(it)  
        }  
    }
}  
  
@Composable  
fun TodoItemBox(todoItem: TodoItem) {  
    Row(  
        modifier = Modifier.fillMaxWidth(0.85f),  
        verticalAlignment = Alignment.CenterVertically,  
    ) {  
        Text(  
            todoItem.title,  
            modifier = Modifier.weight(1f),  
            fontSize = 18.sp,  
            fontWeight = FontWeight.Bold  
        )  
        Text(  
            todoItem.content,  
            modifier = Modifier.weight(2f),  
            fontSize = 14.sp,  
            fontWeight = FontWeight.Light  
        )  
        Checkbox(  
            checked = todoItem.done,  
            onCheckedChange = {},  
        )  
    }  
}

輸入介面

再另外建立一個資料夾,並在裡面建立一個負責輸入介面UI的檔案
從之前的prototype規劃,我們應該使用Column搭配幾個輸入框與一個submit button

@Composable  
fun InputPage() {  
    val groupNameList = listOf("a list", "b list")  
    var title by remember { mutableStateOf("") }  
    var content by remember { mutableStateOf("") }  
    var group by remember { mutableStateOf(groupNameList.first()) }  
    Column(  
        modifier = Modifier.fillMaxSize(),  
        verticalArrangement = Arrangement.spacedBy(20.dp, Alignment.CenterVertically),  
        horizontalAlignment = Alignment.CenterHorizontally  
    ) {  
        TextField(  
            value = title,  
            onValueChange = {  
                title = it  
            },  
            modifier = Modifier.fillMaxWidth(0.8f),  
            singleLine = true,  
            label = {  
                Text("Title")  
            }  
        )  
        TextField(  
            value = content,  
            onValueChange = {  
                content = it  
            },  
            modifier = Modifier.fillMaxWidth(0.8f),  
            label = {  
                Text("Content")  
            }  
        )  
        DropDownSelector(  
            groupNameList,  
            group,  
        ) { group = it }  
        Button(  
            onClick = {}  
        ) {  
            Text("Submit")  
        }  
    }
}  
  
@OptIn(ExperimentalMaterial3Api::class)  
@Composable  
fun DropDownSelector(  
    menu: List<String>,  
    currentDisplay: String,  
    onChosen: (String) -> Unit  
) {  
    var expanded by remember { mutableStateOf(false) }  
  
    ExposedDropdownMenuBox(  
        expanded = expanded,  
        onExpandedChange = { expanded = !expanded }  
    ) {  
        TextField(  
            value = currentDisplay,  
            onValueChange = {},  
            readOnly = true,  
            trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded) },  
            modifier = Modifier  
                .menuAnchor(MenuAnchorType.PrimaryNotEditable)  
                .fillMaxWidth(0.8f),  
            singleLine = true,  
            label = {  
                Text("Group name")  
            }  
        )  
  
        ExposedDropdownMenu(  
            expanded = expanded,  
            onDismissRequest = { expanded = false }  
        ) {  
            menu.forEach { option ->  
                DropdownMenuItem(  
                    text = { Text(option) },  
                    onClick = {  
                        onChosen(option)  
                        expanded = false  
                    }  
                )  
            }  
        }    
	}
}

ExposedDropdownMenu

這是Material3的一個實驗性UI,所以要再開頭加一個annotaion@OptIn(ExperimentalMaterial3Api::class),是原生的選單實現

選單中的TextField

menuAnchor(MenuAnchorType.PrimaryNotEditable)是針對ExposedDropdownMenu的快速modifier設定包,讓TextField可以接管他的狀態
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded) }結尾的Icon

這樣子主要的幾個介面就寫完了,後面有整頁設計的地方不會再給出整段程式碼,但是這個專案應該會在結束前能在Github上看到,剩下的大概只剩下了增加新的group和設定頁面


上一篇
Day 10:多語言設定與圖片資源
下一篇
Day 12:主題與現代Material3,使應用繽紛多彩
系列文
現代Android jetpack compose開發入門12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言