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"
여기서 위와 같이 경로를 설정해두지 않으면 따로 추가한 속성들이 인식이 되지 않는다.
'Android' 카테고리의 다른 글
[안드로이드]안드로이드 앱 모듈화 (0) | 2023.08.13 |
---|---|
[안드로이드]CameraX(Preview, ImageCapture) (0) | 2023.08.06 |
[안드로이드] 서비스 (0) | 2023.07.22 |
[안드로이드] 콘텐츠 제공자(ContentProvider) 와 콘텐츠 리졸버(ContentResolver) (0) | 2023.07.16 |
[안드로이드]FragmentManager (0) | 2023.07.09 |