본문 바로가기
개발(Development)/Android(안드로이드)

[안드로이드] 커스텀 뷰 영역 크기 설정: onMeasure(), setMeasuredDimension()

by 카레유 2021. 8. 22.

안드로이드에서 커스텀 뷰를 만들기 위해서는 보통 아래의 작업 과정을 거친다.

 

1) View 를 상속 받는 커스텀 뷰 클래스를 만들고,

2) onDraw() 메서드를 오버라이드해서 Canvas 객체로 그림를 그린다.

3) 레이아웃 XML파일에 커스텀 뷰를 추가한다.

 

예를 들어 아래와 같이 커스텀 뷰 클래스를 작성하고 띄워보자.

 

<커스텀뷰 클래스 코드>

class CustomViewSize: View {
    
    // 생성자
    constructor(context: Context?) : super(context)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)

    // onDraw() 오버라이드: 그림 그리기
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        // 배경을 초록색으로 채우기
        canvas?.drawColor(Color.GREEN)

        // 파란색 원 그리기
        val paint = Paint()
        paint.color = Color.BLUE
        canvas?.drawCircle(500f, 500f, 200f, paint)
    }
}

 

<레이아웃 XML 코드>

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".CustomViewSizeActivity">

    <com.example.test.CustomViewSize
        android:id="@+id/customViewSize"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
        
</androidx.constraintlayout.widget.ConstraintLayout>

 

<실행 결과>

 

레이아웃 XML파일에서 뷰의 width, height 를 wrap_content 로 설정했지만,

뷰의 전체 영역의 크기는 onDraw()에서 그린 원의 크기보다 크게 출력되었다!

 

안드로이드는 wrap_content 를 보는 순간,

뷰가 자체적으로 본인의 영역을 어느정도 크기로 출력할지 스스로 정한다고 간주한다.

(onDraw()에서 그린 그림의 크기를 알아서 인식해서 뷰의 크기를 결정해주지 않는다!)

 

따라서 커스텀뷰 클래스 내부에서

뷰 자체의 영역이 어느정도 크기가 될지 개발자가 따로 설정해 주어야 한다.


# 뷰 크기 설정 방법

View 클래스는 onDraw()메서드 뿐만 아니라,

onMeasure() 메서드를 통해 뷰의 영역 크기를 설정할 수 있는 방법을 제공한다.

 

onMeausre()메서드는 onDraw()가 호출되기 전에 호출되며,

내부에서 setMeasuredDimension(가로, 세로) 메서드를 호출해주면 뷰의 크기를 원하는대로 세팅해줄 수 있다.

 

*** onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) 메서드 사용방법

- 매개변수로 들어오는 widthMeasureSpec(가로), heightMeasureSpec(세로) 에는 레이아웃 XML에서 설정한 뷰의 크기 정보가 담겨 있다.

- 이를 통해 뷰의 크기와 모드를 확인하는 상세 방법은 아래와 같다.

 

1. 뷰의 크기 값 확인 방법

- 가로: MeasureSpec.getSize(widthMeasureSpec)

- 세로: MeasureSpec.getSize(heightMeasureSpec)

 

2. 뷰의 크기 모드 확인 방법

- MeasureSpec.getMode(widthMeasureSpec) 등으로 아래의 3가지 종류의 크기 모드를 확인할 수 있다.

1) MeasureSpec.AT_MOST: wrap_content -> 뷰 내부에서 자체적으로 뷰의 크기를 결정하라는 의미다.
2) MeasureSpec.EXACTLY: 특정 숫자값, match_parent, fill_parent 등 -> XML에서 설정된 값으로 뷰의 크기가 설정 된다.
3) MeasureSpec.UNSPECIFIED: 설정된 값 없음

 

뷰의 모드와 뷰의 크기를 확인한 다음,

상황에 맞게 setMeasuredDimension(가로, 세로) 메서드를 호출하여 뷰의 크기를 설정해주면 된다.

 

코드로 보면 다음과 같다.

class CustomViewSize: View {

    // 생성자
    constructor(context: Context?) : super(context)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)

    
    // onMeasure() 메서드 오버라이드
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)

        // 뷰 크기 모드 체크
        val viewWidthMode = MeasureSpec.getMode(widthMeasureSpec)
        val viewHeightMode = MeasureSpec.getMode(heightMeasureSpec)

        // 뷰 크기 값 체크
        val viewWidthSize = MeasureSpec.getSize(widthMeasureSpec)
        val viewHeightSize = MeasureSpec.getSize(heightMeasureSpec)


        // 크기 모드에 따라 setMeasuredDimension() 메서드로 뷰의 영역 크기를 설정한다.
        if(viewWidthMode == MeasureSpec.EXACTLY && viewHeightMode == MeasureSpec.EXACTLY){
            // XML에서 뷰의 크기가 특정 값으로 설정된 경우, 그대로 사용한다.
            setMeasuredDimension(viewWidthSize, viewHeightSize)
        }else{
            // wrap_content이거나, 지정되지 않은 경우, 뷰의 크기를 내부에서 지정해주어야 한다
            setMeasuredDimension(1000, 1000)
        }

    }

    
    // onDraw()메서드 오버라이드
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        // 배경을 초록색으로 채우기
        canvas?.drawColor(Color.GREEN)

        // 파란색 원 그리기
        val paint = Paint()
        paint.color = Color.BLUE
        canvas?.drawCircle(500f, 500f, 200f, paint)
    }
}

 

<실행결과>

 

 

설정한대로 뷰의 크기가 지정되어 출력된다.

 

 

 

댓글