어떠한 값에 따라 화면을 다르게 렌더링 하는 뷰가 있다. 클릭에 따라서 그 값이 바뀌면 화면이 달라져야 한다.
그러나 새로 렌더링을 하면서 다시 초기에 설정한 값이 렌더링되기 때문에 바뀐 값이 저장되어야 한다.
따라서 재 렌더링에도 상태를 저장해야 하는 경우에는 mutableStateOf를 사용한다.
var expanded by remember {
mutableStateOf(false)
}
var extraPadding = if(expanded) 48.dp else 0.dp
...
Column(modifier = Modifier.padding(bottom = extraPadding)) {
Text(text = "Hello,")
Text(text = name)
}
remember로 state를 감싸서 변경가능한 상태를 기억해야 한다. 화면 회전에도 대응해야 되는 경우에는 rememberSavable로 설정한다.
참고로 mutablestate의 value인 불 값을 expanded.value가 아닌 그냥 expanded 로 가져오기 위해
property delegation by
를 사용한다. 위처럼 선언하고 import를 해주면 value를 가져오는 setter, getter가 알아서 만들어져 expanded 키워드 자체로 value를 가져올 수 있다.
전체 컴포넌트 코드
@Composable
fun Greeting3(name: String) {
var expanded by rememberSavable {
mutableStateOf(false)
}
var extraPadding = if (expanded) 48.dp else 0.dp
Surface(color = MaterialTheme.colors.secondary, modifier = Modifier.padding(bottom = 4.dp)) {
Row(
modifier = Modifier
.padding(24.dp)
.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween
) {
Column(modifier = Modifier.padding(bottom = extraPadding)) {
Text(text = "Hello,")
Text(text = name)
}
Button(onClick = {
expanded = !expanded
}) {
Text(if (expanded) "show less" else "show more")
}
}
}
}
.verticalScroll(rememberScrollState())
는 스크롤의 상태를 state로 저장한다. 기본적으로 compose는 scrollable하게 되지 않기 때문에
verticalScroll 등의 속성을 지정해준다.
LazyColumn( modifier = Modifier
.fillMaxSize()
.background(color = Color.Green)
// .verticalScroll(rememberScrollState())
){
items(50){
Log.d(TAG, "Greeting4: $it")
Text("name : $it")
}
}
lazycolumn은 item이 화면에 렌더링이 될 때 생성해준다. verticalScroll을 달지 않아도 스크롤이 된다.
위치한 activity의 context를 가져오는 키워드는 LocalContext.current
입니다.
@Composable
fun Greeting6(name: String) {
val textValue = remember {
mutableStateOf("")
}
val context = LocalContext.current
Column(modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
TextField(value = textValue.value, onValueChange = {
textValue.value = it
})
Spacer(modifier = Modifier.padding(10.dp))
Button(onClick = {
Toast.makeText(context, "${textValue.value}", Toast.LENGTH_SHORT).show()
}) {
Text("click")
}
}
}
원래는 위와 같은 방식의 코드로 사용할 수 있지만
@Composable
fun Greeting6(name: String) {
val (a, b) = remember { //a가 get b가 set
mutableStateOf("")
}
val context = LocalContext.current
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
TextField(value = a, onValueChange = {
b (it)
})
Spacer(modifier = Modifier.padding(10.dp))
Button(onClick = {
Toast.makeText(context, "${a}", Toast.LENGTH_SHORT).show()
}) {
Text("click")
}
}
}
mutableState는 Component1이 getValue, Component2가 setValue로 설정되어있으며 이를 바로 구조분해 할당해줄 수 있다. 따라서 위와같이 사용할 수도 있다.
State 관리를 뷰 단에서 하는게 아닌 밖으로 뺄 수 있으면 viewModel에 이를 넣거나 하는 등 관리에 수월할 것이다.
@Composable
fun TextFieldStateChange(){
var fieldVal by remember {
mutableStateOf("")
}
TextFieldOutlinedStateLess(fieldVal = fieldVal, onChange = {
fieldVal = it
})
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TextFieldOutlinedStateLess(fieldVal : String, onChange : (String) -> Unit){
OutlinedTextField(value = fieldVal, onValueChange = onChange)
}
실제로 그려지는 TextFieldOutlinedStateLess
에서는 state에 대한 참조가 없다.
대신에 onChange
라는 함수를 받는데 이 함수가 stated를 바꾸는 작업을 하기 때문에 (onChange = { fieldVal = it }
)
state가 변화되고, TextFieldStateChange() 내부의 TextFieldOutlinedStateLess까지 같이 바뀌는 것이다.