Cute Apple
본문 바로가기
개발/Android

[Android] CustomView 개념 및 사용법

by 미댕댕 2023. 1. 13.

1.  CustomView

안드로이드는 VIewGroup과 View의 계층으로 이루고있다

ViewGroup : n개의 View를 담을 수 있는 컨테이너로 ViewGroup 또한 View를 상속받아 만든 클래스

⇒ LinearLayout, RelativeLayout, FrameLayout, ConstraintLayout

View : View는 액티비티에서 화면을 구성하는 최소 단위로 화면에 보이는 모든것

⇒ ImageView, TextView, EditText, Button

 

Custom View를 사용하는 이유?

  • 일반적인 안드로이드 구성요소로는 원하는 작용이나 애니메이션 또는 UI 를 만들 수 없을떄
  • 재사용성을 위해서

View 가 그려지는 순서

✴️  커스텀뷰를 만들 때 오버라이딩 될 View 클래스의 함수중 제일 중요한 두 함수는 onDraw() 와 onMeasure()

 

1. onMeasure(int widthMeasureSpec, int heightMeasureSpec)

  • 뷰의 크기를 결정해주는 함수
  • 뷰의 크기를 계산하고 width와 height가 결정이 되면 이 함수 안에 setMeasuredDimension(int width, int height) 를 반드시 호출해 주어야 한다.
  • onMeasure은 여러 번 호출될 수 있다. 예를 들어 부모가 자식들의 각 크기를 측정한 뒤, 자식들의 크기의 합이 너무 크거나 작다면다시 measure() 메소드를 호출하여 구체적인 값을 구한다

2. onLayout()

  • 뷰의 위치를 설정해주는 함수이다
  • 뷰의 child들의 크기와 위치를 할당해야 할 때 호출

3. onDraw(Canvas canvas)

  • 요약하면 뷰를 그려주는 함수
  • onDraw() 메소드는 Canvas 라는것을 제공

즉,

도화지 크기를 선택하고(onMeasure), 어느 위치에(onLayout) 어떤 그림을 그릴지(onDraw) 설정

부모가 자식들보다 먼저 그려지고, 형제들은 트리에 나타난 순서대로 그려진다

View Constructor

뷰는 최대 4개의 생성자를 가진다

  • View(Context context)
    • 코드에서 동적으로 뷰를 생성할 때 사용할 수 있는 간단한 생성자이다.
    • 파라미터 context를 통해 현재 실행중인 뷰의 리소스 등에 액세스 할 수 있다
  • View(Context context, AttributeSet attrs)
    • XML로부터 View를 객체를 생성(inflate)할때 사용되는 생성자
  • View(Context context, AttributeSet attrs, int defStyleAttr)
    • XML inflate + theme attribute로 기본 스타일을 적용할때 사용되는 생성자
  • View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
    • XML inflate + theme attribute와 style attribute로 기본 스타일을 적용할때 사용되는 생성자

 

 

커스텀 뷰를 만들고 싶은데 뷰를 확장해서 재정의 하는 방식이 아닌 기존에 있는 뷰를 재정의하여 만들고 싶다면?

가능하다!!


2. Compound Control (복합 컨트롤)

기존 뷰를 상속받아 구현하였을때의 장점

  • Layout을 XML로 정의 할 수 있고, View들을 XML로 정의하여 Layout에 nested 할 수 있다
  • onDraw()와 onMeasure()의 재정의가 딱히 필요하지 않다
  • 빠르게 만들 수 있고, 마치 하나의 component처럼 재활용 할 수 있다

CustomViw의 생성자

customView는 View를 상속받기 때문에 3개의 생성자를 만들어야지 에러가 나지 않는다

그러나 너무 귀찮은작업

class CustomView : ConstraintLayout {
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
}

@JvmOverloads 를 사용하면 아래처럼 간단하게 구현할 수 있다

  • java 에서 제공하는 @JvmOverloads라는 고유한 어노테이션으로 간결한 생성자 오버로딩을 지원
class CustomView @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr)
  • View가 inflate될 때, xml에서 파싱된 속성들은 AttributeSet으로 묶어진다. 이 묶어진 AttributeSet은 혼자만으로는 아무일도 할 수 없다. 그저 키와 값의 모음일 뿐이다.
  • TypedArray를 이용해서 AttributeSet에서 styleable 리소스 attribute에 정의된 녀석들만 가져와야 한다.

그래야 속성에 맞게 정해진 스타일을 입힐 수 있다.

  • defStyleAttr : int 값인데 styleable 리소스 id

 


3. 본격적으로 CustomView  만들어보기‼️

 

1. Custom attributes의 구조를 <declare-styleable> 엘리먼트에서 정의한다

/value/attr.xml 파일을 만들고 아래와 같이 사용할 변수에대한 format 을 정의한다

⚠️ 여기서 주의할 점은 해당 속성의 name 이 android 기본 속성의 이름과 동일하다면 conflict 가 발생하니 조심할 것

<resources>
    <declare-styleable name="CustomView">
        <attr name="bg" format="reference|color" />
        <attr name="icon" format="reference" />
        <attr name="title" format="reference|string" />
    </declare-styleable>
</resources>

 

2. xml로 ui를 작성할 때 attribute의 value값을 정의한다

정의한 attr 값은 xml 에서 속성값으로 사용할 수 있다

<com.example.example.CustomView
        android:id="@+id/cv_github"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:bg="@color/gray"
        app:icon="@drawable/github"
        app:title="github" />

 

 

3. 받아온 attribute value값을 앞서 정의한 클래스에 적용한다

위처럼 속성값을 명시에 주기 위해서는 CustomView에서 정의한 속성을 각 컴포넌트와 매치 시켜 주어야한다

binding.root.context.obtainStyledAttributes(
            attrs,
            R.styleable.CustomView,
            defStyleAttr,
            defStyleRes
        ).apply {
            getResourceId(R.styleable.CustomView_bg, R.color.white).apply {
                binding.clBackground.setBackgroundResource(this)
            }
            getResourceId(R.styleable.CustomView_icon, R.mipmap.ic_launcher_round).apply {
                binding.ivLogo.setImageDrawable(ContextCompat.getDrawable(context, this))
            }
            getString(R.styleable.CustomView_title).apply {
                binding.tvTitle.text = this ?: ""
            }

            recycle()
        }

obtainStyledAttributes?

  • obtainStyledAttributes 함수는 Context의 Theme에서 Style로 지정한 속성 정보 리스트를 가져오는 역할을 한다

recycle?

  • obtainStyledAttributes의 리턴타입인 TypedArray은 recycle()로 garbage collection을 해제시켜 줄 수 있다.
  • 원래라면 자동으로 해주지만 TypedArray는 캐시를 위한 배열을 할당하고 있어 매번 할당해주지 않기 위해 이런 식으로 한다

 

 

 

결과적으로 총 4개의 xml 태그로 아래처럼 커스텀 버튼을 만들 수 있게 되었다!

전체코드는 여기에서 확인👇

https://github.com/sju01334/Example/blob/main/app/src/main/java/com/example/example/CustomView.kt

 

GitHub - sju01334/Example: Android Practice

Android Practice. Contribute to sju01334/Example development by creating an account on GitHub.

github.com

 

참고

https://labs.brandi.co.kr/2021/10/14/jeonhs.html

https://developer.android.com/training/custom-views/create-view?hl=ko

https://wanjuuuuu.tistory.com/entry/Custom-View-만들-때-주의사항-View-생성자

https://pluu.github.io/blog/android/2020/08/09/android-custom-view-style/

 

반응형

댓글