Primary/Compose

Compose TextField를 커스터마이징해 보자

해스끼 2024. 4. 30. 13:19

Compose에는 여러 타입의 Material ``TextField``가 선언되어 있다.

하지만 머티리얼 디자인과는 다른 커스텀 ``TextField``가 필요하다면? ``BasicTextField``를 커스터마이즈하면 된다.

BasicTextField

``BasicTextField``는 모든 머티리얼 ``TextField``의 backbone 컴포넌트이다. 실제로 ``OutlinedTextField`` 등의 코드를 보면, 내부적으로 ``BasicTextField``를 호출하고 있다.

@Composable
fun OutlinedTextField(
	// ...
) {
	// ...
    BasicTextField(
    	// ...
        decorationBox = @Composable { innerTextField ->
            TextFieldDefaults.OutlinedTextFieldDecorationBox(
            	// ...
            )
        }
    )
}

위 코드처럼, ``BasicTextField``의 ``decorationBox`` 매개변수를 통해 텍스트 필드의 여러 속성을 지정할 수 있다. 외곽선이 필요없다면 ``TextFieldDecorationBox``를, 외곽선이 필요하다면 ``OutlinedTextFieldDecorationBox``를 사용하면 된다.

 

매개변수가 매우 많으므로, 실제 적용 예시를 통해 공부해 보자.

Example: 쿠링

쿠링에서는 다음과 같은 디자인을 사용하고 있다. 외곽선이 없으므로 ``TextFieldDecorationBox``를 사용하여 구현해 보자.

placeholder
텍스트 입력

우선 기본 매개변수를 다음과 같이 세팅한다. ``value``는 ``TextField``에 보여줄 text, ``innerTextField``는 Compose 프레임워크가 제공하는 매개변수이다.

decorationBox = { innerTextField ->
    TextFieldDefaults.TextFieldDecorationBox(
        value = query,
        innerTextField = innerTextField,
        // TODO: 이 밑에 매개변수를 추가할 것
    )
},

이제 하나씩 매개변수를 작성해 보자.

// Boolean: TextField 활성화/비활성화
enabled = true,
// Boolean: 텍스트를 1줄로 보여줄 것인가?
singleLine = singleLine,
// 텍스트 변형
// 비밀번호, 전화번호 등을 보여줄 때에는 적절한 변형을 적용해볼 수 있다.
// 지금은 그럴 필요가 없으므로, 어떠한 변형도 적용하지 않는다.
visualTransformation = VisualTransformation.None,
// 텍스트가 비어 있을 때의 placeholder.
// 밑에 따로 선언함. 코드는 생략.
placeholder = {
    Placeholder(placeholderText = placeholderText)
},
// 텍스트 앞에 보여줄 아이콘.
// 마찬가지로 밑에 따로 선언함.
leadingIcon = {
    LeadingSearchIcon()
},
// TextField 맨 끝에 보여줄 아이콘.
// 텍스트가 있을 때에만 모두 지우기 버튼을 보여준다.
trailingIcon = {
    AnimatedVisibility(
        visible = query.isNotEmpty(),
        enter = fadeIn(),
        exit = fadeOut(),
    ) {
        TrailingDeleteIcon(onClear = { onQueryUpdate("") })
    }
},
// TextField에 적용할 색깔.
// 매개변수가 매우 많지만, 아래에서 사용한 정도만 알아둬도 충분할 듯하다.
colors = TextFieldDefaults.textFieldColors(
    textColor = textColor,
    backgroundColor = backgroundColor,
    cursorColor = textColor,
),

완성!

placeholder
text

전체 코드는 아래를 참고.

더보기
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun SearchTextField(
    query: String,
    onQueryUpdate: (String) -> Unit,
    modifier: Modifier = Modifier,
    placeholderText: String = "",
    singleLine: Boolean = true,
    keyboardOptions: KeyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
    keyboardActions: KeyboardActions = KeyboardActions(),
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    contentPadding: PaddingValues = PaddingValues(all = 0.dp),
) {
    val shape = RoundedCornerShape(50)
    val backgroundColor = KuringTheme.colors.gray100
    val textColor = KuringTheme.colors.textBody

    BasicTextField(
        value = query,
        onValueChange = onQueryUpdate,
        modifier = modifier
            .clip(shape)
            .background(backgroundColor, shape = shape),
        textStyle = TextStyle(
            fontSize = 16.sp,
            lineHeight = 24.sp,
            fontFamily = Pretendard,
            fontWeight = FontWeight(400),
            color = textColor,
        ),
        decorationBox = { innerTextField ->
            TextFieldDefaults.TextFieldDecorationBox(
                value = query,
                innerTextField = innerTextField,
                enabled = true,
                singleLine = singleLine,
                visualTransformation = VisualTransformation.None,
                interactionSource = interactionSource,
                placeholder = {
                    Placeholder(placeholderText = placeholderText)
                },
                leadingIcon = {
                    LeadingSearchIcon()
                },
                trailingIcon = {
                    AnimatedVisibility(
                        visible = query.isNotEmpty(),
                        enter = fadeIn(),
                        exit = fadeOut(),
                    ) {
                        TrailingDeleteIcon(onClear = { onQueryUpdate("") })
                    }
                },
                colors = TextFieldDefaults.textFieldColors(
                    textColor = textColor,
                    backgroundColor = backgroundColor,
                    cursorColor = textColor,
                ),
                contentPadding = contentPadding
            )
        },
        singleLine = singleLine,
        keyboardOptions = keyboardOptions,
        keyboardActions = keyboardActions,
    )
}

참고자료

 

androidx.compose.foundation.text  |  Android Developers

androidx.compose.desktop.ui.tooling.preview

developer.android.com