원문 : https://medium.com/over-engineering/getting-started-with-drawing-on-the-android-canvas-621cf512f4c7


안드로이드 Canvas 클래스에 빠지면 당신이 몰랐던 수퍼파워매직한 일들을 할 수 있습니다.
마음속에 있는 도형, 선, 그리고 그림까지 다 그릴수 있는게 상상이 되시나요? 

안드로이드 캔버스가 도와줄거에요.

안드로이드 캔버스는 뭘까요?

Canvas는 안드로이드 플랫폼에 포함되어있는 클래스로써 2차원적인 오브젝트를 화면에 그리는 것을 돕습니다. 

Canvas라는 것이 안드로이드에서 등장한 새로운 개념은 아닙니다. 안드로이드 Canvas는 SKIA라는 2D 그래픽스 오픈 소스 라이브러리에 포함되어있는 SKCanvas 를 랩핑 해둔 클래스 입니다. SKIA는 현재 구글 크롬, 파이어폭스 OS, 플루터, 퓨시아 등에서 사용되고 있습니다. 일단 캔버스가 안드로이드에서 어떻게 동작하는지 이해하고 나면 다른 플랫폼에서도 개념은 똑같기 때문에 이해하기 쉬울겁니다. 

Tip : Canvas에 대한 깊은 이해를 원한다면 SKIA 소스코드를 검색해보세요

근본적으로 SKIA가 안드로이드에서 사용되고 있다는 점을 아는 것은 매우 유용합니다. 뭔가 작업하다가 막히면 SKIA소스코드를 보거나 API 문서 등을 확인할 수 있기 때문이죠.

Canvas 좌표계

안드로이드 캔버스의 좌표계는 좌측 상단 코너로부터 시작됩니다. 이 것을 [0,0] 좌표로 포현하죠. y축의 양수 표현은 아래쪽으로 x축의 양수표현은 오른쪽으로 합니다.

모든 요소들은 캔버스의 [0,0] 상대적 좌표 기준으로 배치 됩니다. 

캔버스를 사용할 때는 픽셀(px)을 사용하며 dp를 사용하지 않습니다. 필요하다면 dp를 px로 변경하는 작업이 필요합니다. 

캔버스의 그리기 명령어는 이전에 그린 요소위에 다시 새로운 요소를 덧 그립니다. 마지막에 그린 것은 캔버스의 가장 위에 위치하게 되는것이죠. 아이템들의 순서에 맞게 올바르게 그려지는것은 개발자 몫입니다.

캔버스를 어떻게 사용하나요?

안드로이드에서 캔버스를 사용하기 위해서는 아래와 같은 것들이 필요합니다.

  1. Bitmap 또는 View : 캔버스에 그려질 픽셀들을 관리
  2. Canvas : 그리기 명령어를 수행하기 위함
  3. 그리기 명령 : 캔버스에 무엇을 그릴 것인지 나타낸다.
  4. Paint : 그리기 명령을 어떻게 꾸밀것인지 묘사합니다.

캔버스 인스턴스에 접근하기

캔버스 인스턴스에 접근하기 위해서는 View클래스를 확장해야합니다. onDraw메소드를 재정의 하여 Canvas를 파라미터로 얻을 수 있기 때문입니다.

class CustomView @JvmOverloads constructor(context: Context,
    attrs: AttributeSet? = null, defStyleAttr: Int = 0)
    : View(context, attrs, defStyleAttr) {

    // 뷰의 내용이 렌더링 될때 호출 됩니다
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        // 여기다가 원하는걸 그리면 됩니다 🙂
    }
}

레이아웃 XML에서 이 커스텀뷰를 사용할 수 있고 자동으로 onDraw가 호출되어 화면에 나타나게 될것입니다.

<com.charlezz.CustomView
        android:layout_width="200dp"
        android:layout_height="300dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

또한 다음과 같이 코드로 캔버스를 만들고 캔버스 객체에 접근할 수도 있어요.

val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) 
val canvas = Canvas(bitmap)

지금 이시점에서는 View없이 캔버스를 코드로 생성해서 사용하는것은 의미가 없어요. 왜냐하면 소프트웨어 렌더링되고 하드웨어 가속을 사용하지 않기 때문이에요. 이것은 몇몇 그리기 명령어에 영향을 줄수 있어요. 예를들면 몇몇 명령어는 하드웨어 렌더링을 지원하지 않고 특정 API레벨에서만 지원하기 때문이죠. 더많은 정보를 얻으러면 하드웨어 가속에 관한 내용을 살펴보면 좋습니다.

캔버스에 무엇을 그릴수 있나요?

많은 것들을 그릴수 있어요. 대부분 많이 그리는게 bitmap 이미지를 캔버스에 그리죠. 이걸 그려주는 매소드가 drawBitmap입니다. Bitmap은 안드로이드 프레임워크에 내장된 Bitmap을 불러올 수 있는 메커니즘을 이용하거나 Glide라이브러리르 사용하면 쉽게 얻을 수 있습니다.

canvas.drawBitmap(bitmap, null, rect, paint)
//비트맵 이미지를 캔버스에 그리기

보시는 코드의 두번째 인자로 그리고 싶은 비트맵의 일부를 전달 할 수 있는데, null을 넘기면 bitmap 전체를 렌더링 하게 됩니다. 세번째 인자는 RectF 객체로 화면에 그리고 싶은 비율과 위치를 결정합니다.

Tip : drawBitmap 메소드에 넘긴 RectF 객체가 정확하게 내가 원하는 비율의 정보인지 잘 확인하셔야합니다. 그렇지 않으면 이미지가 늘어나 찌그러져 보일수 있어요.

네번째 인자는 Paint 객체로 페인트에 관한 내용은 아래에서 다루겠습니다.

Canvas에서 아주 이쁘게 그릴수 있는 명령어가 많습니다. 모든것을 이 포스팅에서 다루지는 않지만 두가지 예제정도만 살펴보겠습니다.

첫번째로, 뷰에 동그란 원을 그리기 위해서는 중심점 좌표값원의 사이즈 그리고 페인트 객체를 준비하면 됩니다.

canvas.drawCircle(x, y, size, paint)

두번째 입니다. drawRect() 메소드인데 화면에 직사각형을 그릴 수 있게 해줍니다.

canvas.drawRect(rect, paint)

캔버스에 그림을 그리는 개념만 잡아보았습니다. 캔버스 문서를 통해 더 많은 정보를 얻고 더 자유롭게 그려보세요.

페인트(Paint)

Paint클래스는 그리기 작업을 아주 이쁘게 꾸며줍니다.

페인트 클래스는 일반적으로 색과 스타일에 대한 정보를 가지고 있습니다. 페인지 객체는 비트맵, 텍스트, 선 등과 같은 객체를 캔버스에 그릴 때 사용됩니다.

페인트 객체를 만들기 위해서는 아래와 같이 코드를 작성합니다.

private val textPaint =
    Paint().apply {
        isAntiAlias = true
        color = Color.RED
        style = Paint.Style.STROKE
    }

이 객체는 반드시 캔버스에서 onDraw()가 호출되기전에 초기화되어야합니다. onDraw()는 빠르게 많이 호출되는데 이곳에서 페인트객체를 초기화하면 리소스가 낭비됩니다.

Tip : isAntiAlias 플래그를 사용하여 가장자리를 매끄럽게 할 수 있습니다.

isAntiAlias 플래그는 상당히 중요합니다. 만약 캔버스에 그린 객체의 가장자리가 삐죽삐죽하다면 해당 플레그를 true로 설정하지 않았기 때문입니다. 이 플레그는 페인트가 가장자리 표면을 캔버스에 매끄럽게 그릴 수 있게 해줍니다.

페인트 클래스는 적어도 세개 이상의 속성을 갖습니다.  페인트로 많은것들을 할 수 있는데, 예를들면 텍스트와 관련된 폰트, 자간, 글자 크기 등의 값을 설정할 수 있습니다.

private val textPaint =
    Paint().apply {
        isAntiAlias = true
        textSize = fontSize
        letterSpacing = letterSpace
        typeface = newTypeface
        setShadowLayer(blurValue, x, y, Color.BLACK)
    }

Tip : 사용하는 Canvase 그리고 Paint API가  다른 API 버전에서도 잘 작동하는지 확인해보세요. API 버전에 따라 작동하지 않는 경우가 있습니다. 

페인트의 setShadowLayer() 메소드는 API버전별로 일관되게 동작하지 않습니다. 캔버스에 텍스트를 그릴때만 동작하고, drawBitmap과 같은 메소드에서는 적용되지 않습니다.

버전별로 일관되게 동작하지 않는 이유는 캔버스 API가 안드로이드 플랫폼 버전에 묶여있기 때문에 OS가 버전이 업데이트 되기 까지는 변경되지 않습니다. 이 페이지의 목록을 확인하면 어떤 안드로이드 버전에서 어떤 API가 동작하는지 확인하실 수 있습니다.

일단 페인트 객체를 만들고 나면 캔버스의 onDraw() 에서 그리기를 수행할 때 페인트객체를 사용할 수 있습니다.

Buy me a coffeeBuy me a coffee
카테고리: AndroidGraphics

0개의 댓글

답글 남기기

이메일은 공개되지 않습니다.