안드로이드는 View, ViewGroup 클래스를 상속받아서 내가 원하는 UI를 만들 수 있다.
기존에 존재하는 뷰들로는 원하는 UI를 구현할 수 없을 때 커스텀을 하게 된다.
처음 커스텀뷰를 만드는 상황에 공식문서의 예제를 보면 뭐라는거야... 라는 소리가 절로 나오기 때문에 작성해보는 글
공식문서
이렇게 생긴 커스텀 뷰를 만들어볼 것이다. 해당 숫자와 텍스트만 바꿔서 여러군데 사용할 수 있다.
class MainTitleView(context: Context, attrs: AttributeSet) : ConstraintLayout(context, attrs) {
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MainTitleView">
<attr name="numberText" format="string" />
<attr name="titleText" format="string" />
</declare-styleable>
</resources>
이 속성은
<com.vocaengplus.vocaengplus.view.MainTitleView
android:id="@+id/wordTitleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:numberText="1"
app:titleText="단어장" />
요렇게 xml에서 사용할 수 있다.
context.theme.obtainStyledAttributes(
attrs,
R.styleable.MainTitleView,
0, 0
).apply {
try {
numberText = getString(R.styleable.MainTitleView_numberText).toString()
titleText = getString(R.styleable.MainTitleView_titleText).toString()
binding?.run {
titleNumberTextView.text = numberText
titleTextView.text = titleText
}
} finally {
recycle()
}
}
그리고 만약 UI 업데이트로 속성값을 변경해야 하거나 값이 필요하다면 해당 값에 접근할 수 있는 메소드도 정의해야 한다.
나는 타이틀로서 UI를 만든거라 UI가 변경될 일이 없어서 만들지 않았다.
1번인 경우는 굳이 onMeasure를 오버라이드하지 않아도 되지만 2번은 오버라이드해서 사이즈를 내가 정해줘야한다.
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val width = resolveSizeAndState(data.size.width.px, widthMeasureSpec, MEASURED_SIZE_MASK)
val height = resolveSizeAndState(data.size.height.px, heightMeasureSpec, MEASURED_SIZE_MASK)
setMeasuredDimension(width, height)
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
x = data.point.x.toFloat()
y = data.point.y.toFloat()
}
init {
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
binding = MainTitleBinding.inflate(inflater, this, true)
context.theme.obtainStyledAttributes(
attrs,
R.styleable.MainTitleView,
0, 0
).apply {
try {
numberText = getString(R.styleable.MainTitleView_numberText).toString()
titleText = getString(R.styleable.MainTitleView_titleText).toString()
elevation = 10f
clipToPadding = false
setBackgroundResource(R.drawable.main_title_background)
binding?.run {
titleNumberTextView.text = numberText
titleTextView.text = titleText
}
} finally {
recycle()
}
}
}
최종결과물