今天大概會聊到的範圍
- State
- Gesture Modifier
在之前,都只是在介紹比較靜態的畫面。但今天如果想要和使用者互動時,該如何處理呢?
從最基礎的按鈕開始,按鈕有一個 parameter 是 onClick,這個 onClick lambda 就可以處理最基礎的互動。
Button(onClick = {}) {
Text("button")
}
在原先的 Android view system,可以對每一個 View 做 click 。在 Compose 中一樣可以做到
Text("button", modifier = Modifier.clickable { })
在任何的 Composable 上,都可以加上 clickable 這個 modifier,一樣可以做到點擊的互動。另外,還有 combinedClickable
這個 modifier ,可以設定長按、雙擊之類較少用的點擊事件。
Text(
"button",
modifier = Modifier.combinedClickable(
onClick = { },
onLongClick = { },
onDoubleClick = { }
)
)
在與使用者互動之後,可能就會有不同的資料需要顯示給 user 。在 Compose function 裡,我們可以用一般的 if-else / when 等判斷式來決定要顯示什麼資料。
var isClicked = false
Column {
if (isClicked) {
// 1.
Text("Click me", modifier = Modifier.clickable { isClicked = true })
} else {
// 2.
Text("Clicked")
}
}
雖然我們在 1 這個 Text 上有設定一個 click 事件,將 isClicked
改成 false。但實際執行起來,會發現點擊了沒有效果。原因是因為 isClicked
這個變數改變後,不會觸發 recomposition。Recomposition 就是 Composable 重新執行 compose 的行為,recomposition 會因為 state 改變而觸發。有幾個不同的方法可建立 state
// 使用 remember 來建立
val isClickedState = remember { mutableStateOf(false) }
// 使用 delegate
var isClicked by remember { mutableStateOf(false) }
在使用 delegate ( by ) 來建立 state 時,會需要 import androidx.compose.runtime.setValue 和 getVale
要使用時,如果用 delegate 的方式建立的 State,可以直接是為對應的型別賦值。如果用 remember 存放 state 的話,會需要透過 value 的 setter
Text("Click me",
modifier = Modifier.clickable {
isClicked = true
// or
isClickedState.value = true
}
)
當一個 composable 中,有自己控制自己的 State 時,就是一個 Stateful composable。Stateful 的 composable 可以自己處理自己的狀態,但同時,外部的元件就沒辦法控制他(或者說,需要一些 callback 來與外部互動)
相反的 Stateless 的 composable 是不自己處理 State 的 component。這不代表他沒有狀態,而可能是他的狀態是由外部控管的。
@Composable
fun StatelessComponent(isClicked: Boolean, whenClick: () -> Unit ) {
Column {
if (isClicked) {
Text("Click me", modifier = Modifier.clickable { whenClick() })
} else {
Text("Clicked")
}
}
}
將 State 交給外部的作法,稱為 state hoisting。透過 state hoisting ,我們可以一層一層將狀態提升到最外層,最後由統一個 ViewModel ( 或 Presenter 這類的角色 ) 進行控制。
今天先簡單題及了互動與狀態 (State),後續還會有更複雜的互動方式(例如 Drag & Drop),State 也會是動畫、互動等行為的基礎。
Reference: