본문 바로가기
Android/안드로이드 기본지식

[Android] Canvas를 이용하여 UI 그리기

by 붕어사랑 티스토리 2022. 4. 14.
반응형

https://developer.android.com/training/custom-views/custom-drawing

 

맞춤 그리기  |  Android 개발자  |  Android Developers

맞춤 뷰에서 가장 중요한 부분은 디자인입니다. 맞춤 그리기는 애플리케이션의 필요에 따라 쉽거나 복잡할 수 있습니다. 이 과정에서는 가장 일반적인 작업 몇 가지에 관해 설명합니다. 맞춤 뷰

developer.android.com

 

먼저 이를 배우기 전에 Custom View에 대한 대략적인 개념을 학습하고 오자

 

https://lucky516.tistory.com/173

 

[Android] Custom View 만들기

https://developer.android.com/training/custom-views/create-view 뷰 클래스 만들기  | Android 개발자  | Android Developers 잘 설계된 맞춤 뷰는 잘 설계된 다른 클래스와 매우 유사합니다. 즉, 사용하기..

lucky516.tistory.com

 

 

 

 

 

1. onDraw() 오버라이드

 

커스텀 UI를 만들때 가장 중요한것은 당연히 onDraw()를 오버라이드 하는 것이다.

 

UI를 그릴 때 사용하는 객체는 Canvas이다. Canvas를 이용하면 텍스트, 선, 비트맵 및 기타 여러 그래픽 프리미티브를 그리기위한 메서드를 정의한다.

 

허나 Canvas를 이용하기전에 먼저 Paint라는 것을 만들어야 한다.

 

 

 

2. Drawing Objects 생성

 

android.graphics 프레임워크는 drawing을 두가지 영역으로 나눈다

 

  • Canvas, 무엇을 그릴지를 나타냄
  • Paint, 어떻게 그릴것인지를 나타냄

예를들어 Canvas는 선을 그리면, Paint는 그 선이 어떠한 색으로 결정 될지 나타내는 것이다.

 

 

고로 Canvas를 통하여 어떤 UI를 그리기 전에 Paint 객체를 생성하여 어떻게 그려질 것인지를 나타내야 한다.

 

private void init() {
   textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
   textPaint.setColor(textColor);
   if (textHeight == 0) {
       textHeight = textPaint.getTextSize();
   } else {
       textPaint.setTextSize(textHeight);
   }

   piePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
   piePaint.setStyle(Paint.Style.FILL);
   piePaint.setTextSize(textHeight);

   shadowPaint = new Paint(0);
   shadowPaint.setColor(0xff101010);
   shadowPaint.setMaskFilter(new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL));

   ...

 

 

 

3. 최적화 문제

UI를 다시 그리는 것은 매우 자주 반복된다. 여기서 어떻게 그려질지를 매번 onDraw()에 정의한다면?

UI를 그릴 때 마다 매번 객체를 생성하는것은 퍼포먼스에 매우 안좋은 영향을 준다.

 

고로 따로 함수를 빼 내어 init() 같은 이름으로 따로 처리하여 미리 정의해 두는 것 이 좋다.

 

 

 

 

 

 

4. 레이아웃 이벤트 다루기

 

커스텀UI를 적절히 그리기 위해서는, UI의 사이즈를 정확히 아는것이 중요하다.

복잡한 커스텀뷰는 화면영역의 크기와 모양에따라 레이아웃계산을 여러번 하는 경우가 많다.

뷰가 화면에 차지하는 크기를 추측해서는 안된다. 하나의 앱에서만 뷰를 사용한다 해도 멀티스크린이나 가로모드 세로모드등 다양한 케이스가 많이 때문이다.

 

 

 

View 클래스는 크기를 측정하는 많은 메소드들을 가지고 있지만, 보통 대부분의 경우에 오버라이드 할 필요가 없다.

뷰사이즈에 특별한 작업이 필요하지 않는 한, 대게 onSizeChanged() 메소드만 재정의 해 주면 된다.

 

 

처음 뷰의 크기가 할당되면 onSizeChanged()가 호출되고, 뷰의 크기가 변하면 다시 호출된다.

위치, 크기등 뷰의 사이즈와 관련된 값은 모두 onSizeChanged()에서 계산해야 하며 onDraw()에서 하지 않아야 한다.

 

 

 

뷰의 레이아웃의 매개변수를 좀더 세밀하게 제어하고 싶다면 아래와 같은 형태로 onMeasure() 메소드를 재정의 하면 된다

 

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
       // 최대한 작게 계산하기, 
       int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();
       int w = resolveSizeAndState(minw, widthMeasureSpec, 1);

       // 최대한 크계 계산하기
       int minh = MeasureSpec.getSize(w) - (int)mTextWidth + getPaddingBottom() + getPaddingTop();
       int h = resolveSizeAndState(MeasureSpec.getSize(w) - (int)mTextWidth, heightMeasureSpec, 0);

       setMeasuredDimension(w, h);
    }

 

  • resolveSizeAndState 함수는 최종 너비 및 높이값을 만드는데 사용된다. 이 메소드는 onMeasure() 메소드에 전달된 MeasureSpec(여기서 widthMeasureSpec, heightMeasureSpec)과 원하는 크기를 비교하여 View.MeasureSpec값을 반환한다

 

  • onMeasure함수는 리턴값이 없다. 대신 이 메소드는 반드시 setMeasureDimension(width, height) 를 호출해 결과를 전달해야 한다. 안그러면 익셉션을 발생시킨다.

 

 

 

 

5. 본격적으로 그리기!

 

onDraw() 메소드는 다음과 같은 기능이 있다

 

  • drawText()를 활용하면 텍스트를 그릴 수 있다. setTypeface()를 호출하여 글꼴을 지정하고, setColor()를 호출하여 색상을 지정한다
  • drawRect(), drawOval(), drawArc() 를 이용하여 네모, 타원, 호 등을 그릴 수 있다. setStyle()을 사용하여 윤곽선만 그리는지 도형을 채우는지 등 정할 수 있다
  • 원하는 모양이 미리 정의된게 없으면 Path 클래스를 이용하자 Path객체에 선과 곡선들을 추가한 다음 drawPath()를 사용하면 원하는 도형을 그릴 수 있다. 마찬가지로 setStyle()로 윤곽선만 표시하거나 색상을 채우거나 또는 둘다 할 수 있다
  • LinearGradient 객체를 활용하여 그라데이션을 정의 해 줄 수 있다. 그라데이션을 사용하고 싶으면 setShader()를 호출한다
  • drawBitmap()을 사용하여 비트맵을 그린다

 

 

아래 예제는 대표적으로 Canvas를 사용하여 UI를 그리는 모습을 나타낸다

    protected void onDraw(Canvas canvas) {
       super.onDraw(canvas);

       // Draw the shadow
       canvas.drawOval(
               shadowBounds,
               shadowPaint
       );

       // Draw the label text
       canvas.drawText(data.get(currentItem).mLabel, textX, textY, textPaint);

       // Draw the pie slices
       for (int i = 0; i < data.size(); ++i) {
           Item it = data.get(i);
           piePaint.setShader(it.shader);
           canvas.drawArc(bounds,
                   360 - it.endAngle,
                   it.endAngle - it.startAngle,
                   true, piePaint);
       }

       // Draw the pointer
       canvas.drawLine(textX, pointerY, pointerX, pointerY, textPaint);
       canvas.drawCircle(pointerX, pointerY, pointerSize, mTextPaint);
    }

 

 

 

 

 

반응형

댓글