[Android] Jetpack Compose : Reusable Composable

Minji Jeong·2022년 8월 11일
0

Android

목록 보기
31/39
post-thumbnail
컴포즈를 사용해서 화면을 만들 때, 화면을 구성하는 UI 요소가 많아지면 많아질수록 소스코드도 그만큼 늘어나게 된다. 만약 동일한 형태의 UI 요소를 여러개 사용할 경우 불필요한 코드만 늘어날 뿐인데, 기존의 XML과는 다르게 컴포즈로 화면을 만들면 특정 UI 요소를 재사용 할 수 있기 때문에 불필요한 코드를 줄일 수 있다. 나도 최근까지 이러한 장점을 인지하지 못한 채 무작정 코드만 작성하고 있었는데, 사용해보니 코드를 깔끔하게 쓸 수 있을 뿐만 아니라 가독성도 좋아져서 해당 방법을 공유하고자 이렇게 포스팅을 남기게 되었다. 해당 포스팅에선 재사용 가능한 버튼, 텍스트 필드에 대해 소개하고자 한다.

1. Reusable Button


먼저 이렇게 위 사진처럼 버튼 세개로 구성된 화면을 만들고자 할 때, 보통은 컴포저블 안에 버튼 세개를 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
        )
    }
}

2. Reusable TextField

이번엔 재사용 가능한 텍스트필드를 만들어보자.

@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 요소들을 재사용 가능한 컴포저블로 만들 수 있다. 만드는 방법은 버튼과 텍스트필드를 만들었던 것과 별반 다를 게 없으니, 여러 화면에서 자주 호출되는 컴포저블이 있다면 재사용 가능하게 만들어서 불필요한 코드를 줄여보도록 하자.

Reference

https://stackoverflow.com/questions/69885391/how-to-make-reusable-components-in-jetpack-compose

profile
Mobile Software Engineer

0개의 댓글