먼저 이렇게 위 사진처럼 버튼 세개로 구성된 화면을 만들고자 할 때, 보통은 컴포저블 안에 버튼 세개를 Column 내에 배치하려고 할 것이다. 해당 방식으로 화면을 구현했을 때의 코드는 아래와 같다.
@Composable
fun TestComposable() {
Column(modifier = Modifier
.fillMaxSize()
.background(colorResource(R.color.lightOrange))
.padding(20.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(
onClick = {},
modifier = Modifier
.fillMaxWidth()
.height(80.dp),
elevation = null,
colors = ButtonDefaults.buttonColors(
backgroundColor = colorResource(R.color.white),
contentColor = colorResource(R.color.lightOrange))
) {
Text(
text = stringResource(R.string.btn1),
fontSize = 17.sp
)
}
Spacer(Modifier.height(10.dp))
Button(
onClick = {},
modifier = Modifier
.fillMaxWidth()
.height(80.dp),
elevation = null,
colors = ButtonDefaults.buttonColors(
backgroundColor = colorResource(R.color.white),
contentColor = colorResource(R.color.lightOrange))
) {
Text(
text = stringResource(R.string.btn2),
fontSize = 17.sp
)
}
Spacer(Modifier.height(10.dp))
Button(
onClick = {},
modifier = Modifier
.fillMaxWidth()
.height(80.dp),
elevation = null,
colors = ButtonDefaults.buttonColors(
backgroundColor = colorResource(R.color.white),
contentColor = colorResource(R.color.lightOrange))
) {
Text(
text = stringResource(R.string.btn3),
fontSize = 17.sp
)
}
}
}
버튼 3개를 배치했을 때도 코드가 상당히 길어지는데, 버튼의 개수가 10개 20개 된다면 코드가 훨씬 길어질 것이다. 하지만 코드가 길어지는 것보다 더 아쉬운 건, 형태가 동일한 버튼 여러개를 그리고 있는 것이다. 사이즈만 동일할 뿐만 아니라 배경색과 텍스트 색상까지 모두 동일하다. 이렇게 형태가 비슷한 컴포저블(버튼 뿐만 아니라 텍스트, 텍스트필드, 이미지 등)들은 재사용 가능한 하나의 컴포저블을 따로 만든 후, 해당 컴포저블을 호출하는 방식으로 구현할 수 있다. 이렇게 구성하면 우리는 불필요한 코드를 줄일 수 있다.
위 예제의 버튼들을 그렸던 코드를 다시 보자.
Button(
onClick = {},
modifier = Modifier
.fillMaxWidth()
.height(80.dp),
elevation = null,
colors = ButtonDefaults.buttonColors(
backgroundColor = colorResource(R.color.white),,
contentColor = colorResource(R.color.lightOrange))
) {
Text(
text = "Button 2",
fontSize = 17.sp
)
}
해당 코드를 참고해 재사용 가능한 버튼 컴포저블을 만들자면 다음과 같이 만들 수 있다. 변경되어야 하는 건 텍스트와 클릭시 발생할 이벤트 뿐이니, 두 변수에 대해서만 파라미터를 정의하면 된다.
@Composable
fun ColorButton(
@StringRes text: Int,
action: () -> Unit) {
Button(
onClick = action,
modifier = Modifier
.fillMaxWidth()
.height(80.dp),
elevation = null,
colors = ButtonDefaults.buttonColors(
backgroundColor = colorResource(R.color.lightOrange),
contentColor = colorResource(R.color.white))
) {
Text(
text = stringResource(text),
fontSize = 17.sp
)
}
}
이제 위 ColorButton을 활용해서 버튼 세개를 구현해보자. 확실히 아까보다 불필요한 코드들이 감소했고, 가독성도 더 좋아졌다.
@Composable
fun TestComposable() {
Column(modifier = Modifier
.fillMaxSize()
.background(colorResource(R.color.lightOrange))
.padding(20.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
ColorButton(R.string.btn1) { /*onClick*/ }
Spacer(Modifier.height(10.dp))
ColorButton(R.string.btn2) { /*onClick*/ }
Spacer(Modifier.height(10.dp))
ColorButton(R.string.btn3) { /*onClick*/ }
}
}
하지만 일부 속성들은 다른 버튼을 여러개 사용하고 싶을 수도 있다. 예를 들어 사이즈, 배경색, 텍스트 색상은 다른 버튼을 재사용하고자 한다면 modifier, backgroundColor, contentColor에 대한 파라미터를 정의해주고, 버튼과 텍스트에서 전달받은 속성들을 사용하면 된다.
@Composable
fun ColorButton(
@StringRes text: Int,
modifier: Modifier,
@ColorRes backgroundColor: Int,
@ColorRes contentColor: Int,
action: () -> Unit) {
Button(
onClick = action,
modifier = modifier,
elevation = null,
colors = ButtonDefaults.buttonColors(
backgroundColor = colorResource(backgroundColor),
contentColor = colorResource(contentColor))
) {
Text(
text = stringResource(text),
fontSize = 17.sp
)
}
}
이번엔 재사용 가능한 텍스트필드를 만들어보자.
@Composable
fun TestComposable(
email: String,
name: String,
pw: String,
onEmailChange: (String) -> Unit,
onNameChange: (String) -> Unit,
onPwChange: (String) -> Unit,
) {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(R.color.lightRed))
.padding(20.dp)
) {
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = email,
onValueChange = onEmailChange,
label = {
Text(
text = stringResource(R.string.email),
color = Color.White
)
},
colors = TextFieldDefaults.outlinedTextFieldColors(
textColor = Color.White,
focusedBorderColor = Color.White,
unfocusedBorderColor = Color.White
)
)
Spacer(Modifier.height(10.dp))
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = name,
onValueChange = onNameChange,
label = {
Text(
text = stringResource(R.string.name),
color = Color.White
)
},
colors = TextFieldDefaults.outlinedTextFieldColors(
textColor = Color.White,
focusedBorderColor = Color.White,
unfocusedBorderColor = Color.White
)
)
Spacer(Modifier.height(10.dp))
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = pw,
onValueChange = onPwChange,
label = {
Text(
text = stringResource(R.string.password),
color = Color.White
)
},
colors = TextFieldDefaults.outlinedTextFieldColors(
textColor = Color.White,
focusedBorderColor = Color.White,
unfocusedBorderColor = Color.White
)
)
}
}
위 예제 역시 아까 버튼 처럼 동일한 형태의 텍스트 필드가 여러개 사용되고 있다. 재사용 가능한 버튼 컴포저블을 만든 것처럼 재사용 가능한 텍스트 필드를 만들어보자. 주의해야 할 것은 입력한 텍스트를 새 값으로 반환해주는 콜백인 onValueChange도 파라미터로 정의해야 한다는 것이다.
@Composable
fun BasicTextField(
@StringRes text: Int,
value: String,
onValueChange: (String) -> Unit
) {
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = value,
onValueChange = onValueChange,
label = {
Text(
text = stringResource(text),
color = Color.White
)
},
colors = TextFieldDefaults.outlinedTextFieldColors(
textColor = Color.White,
focusedBorderColor = Color.White,
unfocusedBorderColor = Color.White
)
)
}
재사용 가능한 텍스트필드를 활용해서 다시 화면을 작성했다. 이전보다 불필요한 코드들이 확연히 감소된 것을 눈으로 확인할 수 있다.
@Composable
fun TestComposable(
email: String,
name: String,
pw: String,
onEmailChange: (String) -> Unit,
onNameChange: (String) -> Unit,
onPwChange: (String) -> Unit,
) {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(R.color.lightRed))
.padding(20.dp)
) {
BasicTextField(R.string.email,email, onEmailChange)
Spacer(Modifier.height(10.dp))
BasicTextField(R.string.name, name, onNameChange)
Spacer(Modifier.height(10.dp))
BasicTextField(R.string.password, pw, onPwChange)
}
}
이것 말고도 툴바, 이미지, 카드 등 여러 UI 요소들을 재사용 가능한 컴포저블로 만들 수 있다. 만드는 방법은 버튼과 텍스트필드를 만들었던 것과 별반 다를 게 없으니, 여러 화면에서 자주 호출되는 컴포저블이 있다면 재사용 가능하게 만들어서 불필요한 코드를 줄여보도록 하자.