Android

[안드로이드] CustomTextView

나맘임 2023. 7. 30. 16:25

CustomView이란?

안드로이드에서 기본적으로 제공하는 TextView, EditText, Button 등은 당연하게도 설정할 수 있는 속성값들이 정해져있다.

만약 사용자가 제공하지 않는 기능을 만들고 싶다면 사용하는 것이 CustomView이다.

 

이 글에선 AppCompatTextView 클래스를 상속 받은 CustomTextView를 만들고자 한다.

CustomTextView 구현 과정

1. 뷰를 정의한다.

values 패키지에 attrs.xml로 파일을 생성한다.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomText">
        <attr name="prefixText" format = "string"/>
        <attr name="showText" format="boolean"/>
    </declare-styleable>
</resources>

CustomTextView의 이름을 CustomText라 짓고 추가할 속성들의 이름을 prefixText, showText 로 각각 정의하였다.

 

해당 속성에 맞는 format을 사용해주면 된다.

 

만약 enum 값을 사용하고 싶다면 다음과 같이 정의해도 된다.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomText">
        <attr name="prefixText" format = "string"/>
        <attr name="showText" format="enum">
            <enum name="visible" value="true"/>
            <enum name="invisible" value="false"/>
        </attr>
    </declare-styleable>
</resources>

만약 attr의 name를 설정할 때 오류가 발생한다면 일단 작성한 후 빌드를 하면 해결이 된다.

 

2. AppCompatTextView의 서브 클래스를 만든다.

package com.example.myapplication2

import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView

class CustomText : AppCompatTextView {
    constructor(context:Context): super(context){

    }

    constructor(context: Context, attrs: AttributeSet) : super(context,attrs){
        val typed = context.obtainStyledAttributes(attrs, R.styleable.CustomText)
        val size = typed.indexCount

        for (i in 0 until size){
            when(typed.getIndex(i)){
                R.styleable.CustomText_prefixText ->{
                    val prefixText = typed.getString(R.styleable.CustomText_prefixText) ?: ""
                    addPrefix(prefixText)
                }

                R.styleable.CustomText_showText ->{
                    val showText = typed.getBoolean(R.styleable.CustomText_showText, true)
                    if(showText)
                        visibility = VISIBLE
                    else
                        visibility = INVISIBLE

                }
            }
        }

    }

    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super (context, attrs, defStyleAttr){

    }

    private fun addPrefix(prefix : String){
        setText("[${prefix}] ${text}")
    }
}

먼저 AppCompatTextView는 3 가지의 생성자를 요구하기 때문에 생성자를 작성해주었다.

 

지금 상황에선 필요한 것은 두번째 생성자로 context는 app의 context를,

attrs는 그 CustomView가 가지고 있는 속성들을 Set으로 저장되어 있는 것으로 여기서 속성의 값을 가져와 뷰를 수정한다.

 

val typed = context.obtainStyledAttributes(attrs, R.styleable.CustomText)

AttributeSet에서 바로 값을 가져와 수정해도 되지만 그럴 경우 다음과 같은 문제가 발생한다.

 

1. 속성 값 내의 리소스 참조가 결정되지 않음

2. 스타일이 적용되지 않음.

 

그렇기 때문에 공식 문서에선 context에 obtainStyleAtrributes()으로 AttributeSet을 전달하여 내부에 구현된 TypeArray 배열을 리턴받아 사용하기를 권장한다.

 

typed 변수가 TypeArray 배열을 나타내는 것이다.

 

그리고 한 뷰에 여러 가지 속성들은 존재하기 때문에 when 절을 사용해 속성들을 구분하고 그 속성에 맞는 값 처리를 해준다.

        for (i in 0 until size){
            when(typed.getIndex(i)){
                R.styleable.CustomText_prefixText ->{
                    val prefixText = typed.getString(R.styleable.CustomText_prefixText) ?: ""
                    addPrefix(prefixText)
                }

                R.styleable.CustomText_showText ->{
                    val showText = typed.getBoolean(R.styleable.CustomText_showText, true)
                    if(showText)
                        visibility = VISIBLE
                    else
                        visibility = INVISIBLE

                }
            }
        }
        
    private fun addPrefix(prefix : String){
        setText("[${prefix}] ${text}")
    }

그 과정이 위 코드이다.

 

prefixText 경우 format을 String으로 했기 때문에 getString 메소드를 사용했고 만약 속성이 없다면 빈값을 넣어줬다.

 

showText는 visibility를 결정하기 때문에 View의 visibility 값을 수정해주었다.

 

3. CustomTextView 사용해보기

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".MainActivity">

    <com.example.myapplication2.CustomText
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="유저"
        android:textAlignment="center"
        app:prefixText="테스트"
        android:textSize="30dp"/>


</androidx.coordinatorlayout.widget.CoordinatorLayout>
xmlns:app="http://schemas.android.com/apk/res-auto"

여기서 위와 같이 경로를 설정해두지 않으면 따로 추가한 속성들이 인식이 되지 않는다.