사이드프로젝트(withPet) #3 - 커스텀 위젯 [text input] #1

김민태·2024년 6월 16일

withPet

목록 보기
3/6
post-thumbnail

CustomEditText란?

EditText를 상속받아 입력 값을 검증하고, 이에 따라 UI를 동적으로 변화시키는 텍스트 입력 컴포넌트입니다.

CustomEditText 소스 먼저 보기


개발 전 앱의 초반 프로세스 간단 설명

  • 인트로: 앱의 모듈, 권한, 버전 관리 등의 초기 세팅을 진행
  • 회원가입 및 로그인: 입력과 검증 기능이 필요한 화면이며, 주로 EditTextButton 같은 기본 뷰들을 사용하여 동작.

입력 폼에서 EditText와 같은 기본 제공 뷰들은 원시적인 기능만 제공하기 때문에, 더 나은 UI/UX를 위해 상태 변화에 따른 디자인과 기능을 추가하여 컴포넌트화하는 작업이 필요했습니다.


1. 커스텀 클래스 책임 분리

입력 폼에 대한 컴포넌트는 두 개의 클래스에 분리하여 작성했습니다.
1. CustomEditText: AppCompatEditText를 상속받아 텍스트 입력 기능에 집중.
2. CustomInput: LinearLayout을 상속받아, EditText 외적인 UI 요소를 추가한 레이아웃.

이로 인해 CustomEditText는 텍스트 입력 및 검증 기능을 담당하고, CustomInput은 추가적인 레이아웃과 UI 관련 작업을 담당합니다. 대부분의 화면에서 CustomInput을 사용하지만, CustomEditText도 단독으로 사용 가능하며 두 클래스 간 호환성을 유지하도록 설계했습니다.

open class CustomEditText @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : AppCompatEditText(context, attrs, defStyleAttr)
class CustomInput @JvmOverloads constructor(
    context: Context, 
    attrs: AttributeSet? = null, 
    defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr)

2. 백그라운드 UI 디자인

기획 담당 팀원으로부터 피그마 디자인이 제공되었고, 그에 따른 요구 사항은 다음과 같습니다:

  • 앱의 primary 색상을 가진 밑줄 디자인.
  • 값 검증 기능.
  • error 상태에 대한 UI 확장.

2-1. 밑줄 디자인 추가

EditText에 포커스가 가거나 값 검증 결과에 따라 밑줄 색상을 동적으로 변경하는 기능을 추가했습니다.

init {
        setBackgroundResource(R.drawable.bg_custom_input)

        setOnFocusChangeListener { _, hasFocus ->
            if (hasFocus) {
                setUnderlineColor(R.color.primary)
            } else {
                if (!isValid()) { // <- 1-2-2에서 다루겠습니다.
                    setUnderlineColor(R.color.error)
                } else {
                    setUnderlineColor(R.color.disable)
                }
            }
        }  
        ... 다른 메서드들
}

밑줄의 색상 변경은 setUnderlineColor() 메서드를 통해 이루어지며, 이를 통해 사용자가 입력 중인 상태인지, 입력 값이 유효한지 시각적으로 알 수 있게 했습니다. TextView의 기본 제공 메서드인 setError()를 오버라이드하여 에러 발생 시 밑줄 색상도 함께 변경하도록 처리했습니다.

2-2. validateListener 구현, Error 상태와 연계

텍스트 입력 폼의 값 검증에러 상태를 연동하여 사용자가 입력 오류를 쉽게 인지하도록 했습니다.

입력 폼마다 다른 유효성 검사 규칙을 적용할 수 있도록 IsValidListener 인터페이스를 만들어, 외부에서 다양한 검증 로직을 지정할 수 있게 설계했습니다.

interface IsValidListener {
    fun isValid(text: String): Boolean
}

그리고 CustomEditText 내부에서 입력 값 변화에 따라 isValid() 메서드가 호출되도록 구현하여, 사용자 입력에 따라 동적으로 UI가 변경되도록 했습니다.

 override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
        val text = s.toString()
        if (isValid(text)) {
            setUnderlineColor(R.color.primary)
        } else {
            setUnderlineColor(R.color.error)
        }
    }
 
 private fun isValid(): Boolean {
        val text = text.toString()
        return isValidListener?.isValid(text) ?: text.isNotEmpty()
}

이를 통해 텍스트 입력 필드의 유효성 검사를 외부에서 확장 가능하게 했으며, 아래와 같은 방식으로 CustomInput에서 사용할 수 있습니다.

binding.etAge.setIsValidListener(object : IsValidListener {
    override fun isValid(text: String): Boolean {
        val ageText = if (text.isEmpty()) 0 else text.toInt()
        updateButtonState() 
        return ValidationUtils.isValidAge(ageText)
    }
})

결과적으로, 입력에 대한 값 검증과 이에 따른 UI 변경이 이루어져 사용자가 명확하게 입력 오류를 인식할 수 있게 되었습니다.

Validation Example


3줄 요약

  1. CustomEditTextCustomInput 클래스를 통해 입력 필드의 기능과 UI 책임을 분리했습니다.
  2. 값 검증 로직과 UI 변경을 연동해 사용자가 입력 상태를 시각적으로 인지할 수 있도록 설계했습니다.
  3. 이를 통해 입력 오류에 대한 명확한 피드백을 제공하고, 확장 가능한 유효성 검증을 구현했습니다.

라벨과 에러 문구에 해당하는 UI 확장 구현은 2편에서 다루겠습니다. (이동)

0개의 댓글