본문 바로가기
개발/안드로이드

[Android] 출력 : 캔버스 (Canvas, Paint)

by darksilber 2011. 6. 23.
반응형

출처 - http://blog.naver.com/PostView.nhn?blogId=q1q3q5&logNo=10099496049

<뷰와 캔버스의 이해>

뷰와 캔버스가 헷갈리시는 분들을 위해 준비했다.

한마디로 캔버스는 뷰에 그려주는 것이다.

그림에서 보여지듯이 View 위에 Canvas가 장미를 그려주는 것이다.

훗날 이미지를 회전시키는 것 또한 View 자체를 돌리는 것이 아니라 Canvas를 돌리는 것이라 이해하면 되겠다.

아무튼 View와 Canvas의 차이를 이제 이해했으리라.

<커스텀 뷰>

안드로이드가 제공하는 레이아웃과 위젯만 해도 상당한 편이나, 다른 프로그램이 그렇듯 표준만으로 원하는 모든 것을 만들 수 없다.

커스텀이란 입맛대로 만든다는 뜻이며, 모양과 기능을 마음대로 디자인 할 수 있어 상상하는 모든 것을 다 만들 수 있다.

커스텀 위젯을 만드는 기본적인 방법은 객체 지향으 상속 기법을 활용하는 것이다.

그러나 방법이 복잡하고 선행 지식을 많이 요구하므로, 아직 그것을 논할 단계는 아니다.

이 장의 주제는 커스텀 위젯이 아니라 뷰의 표면에 원하는 그림을 그려내는 것이다.

커스텀 위젯의 기본 구조는 다음과 같다.

import android.app.*;
import android.content.*;
import android.graphics.*;
import android.os.*;
import android.view.*;

public class CustomView extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyView vw = new MyView(this);
setContentView(vw);
}

protected class MyView extends View {
public MyView(Context context) {
super(context);
}

public void onDraw(Canvas canvas) {
Paint Pnt = new Paint();
Pnt.setColor(Color.BLUE);
canvas.drawColor(Color.WHITE);
canvas.drawCircle(100,100,80,Pnt);
}
}
}

View로부터 상속을 받는 MyView 클래스를 정의했는데, 이 클래스가 바로 클래스 뷰다.

(외부 클래스로 정의해도 상관없다.)

onDraw에 어떤 코드를 작성하는가에 따라 출력되는 모양이 달라진다.

생각보다 자유도가 굉장히 높은 편이다. 레이아웃과 달리 픽셀 단위까지 섬세하게 지정할 수 있기 때문이다.

이후의 예제도 구조는 동일하며 onDraw의 출력 코드만 달라진다.

<Canvas>

점, 선, 원, 사각형 등 기본 도형을 그리는 메서드들이다.

void drawPoint(x, y, paint) : (x,y) 좌표에 점을 찍는다.

void drawLine(startX, startY, stopX, stopY, paint) : (x,y) 좌표부터 (x,y) 좌표에 선을 그린다.

void drawCircle(x, y, radius, paint) : (x,y) 좌표에 반지름(radius)만큼 원을 그린다.

void drawRect(left, top, right, bottom) : (left, top)와 (right, bottom)의 대각선을 기초로 사각형을 그린다.

void drawText(text, x, y, paint) : (x,y) 좌표에 문자를 출력한다.

void Paint.setColor(int color) : 그리기 색상

void drawARGB(a, r, g, b) : 기존 색상에 alpha가 추가된 것. (투명 : 0xff / 불투명 : 0x00)

void drawRGB(r, g, b) : 0~255까지 각 색상의 코드를 적으면 됨.

void drawColor(int color) : RED, BLUE 등 원하는 색상을 적으면 된다.

void drawPaint(paint) : 설정 값을 적용, 단순한 채우기

void drawRoundRect(RectF r, rx, ry, paint)

rx & ry : 모서리의 둥근 정도. 가상의 타원으로 두 값이 클수록 모서리가 둥글다.

void drawOval(RectF r, paint) : Oval은 달걀모양, 지정한 사각형 영역에 내접한 원을 그린다.

void drawArc(RectF r, startAngle, sweepAngle, boolean useCenter, paint)

startAngle : 3시방향이 0도, 시계방향으로 증가

sweepAngle : 그리는 각도 크기

useCenter : (T) 부채꼴, (F) 호

void drawLines(float[] pts, paint) : 배열에 저장된 선을 순서대로 그린다. 연결하고 싶으면 시작과 끝 점을 일치시킨다.

void drawPoints(float[] pts, paint) : 배열상의 좌표를 꺼내 여러 개의 점을 찍는다.

이제 예제를 통해 어떤 식으로 출력이 되는지 알아보자.

public class Primitive1 extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyView vw = new MyView(this);
setContentView(vw);
}

protected class MyView extends View {
public MyView(Context context) {
super(context);
}

public void onDraw(Canvas canvas) {
Paint Pnt = new Paint();
canvas.drawColor(Color.WHITE);
canvas.drawPoint(10,10,Pnt);
Pnt.setColor(Color.BLUE);
canvas.drawLine(20,10,200,50,Pnt);
Pnt.setColor(Color.RED);
canvas.drawCircle(100,90,50,Pnt);
Pnt.setColor(0x800000ff);
canvas.drawRect(10,100,200,170,Pnt);
Pnt.setColor(Color.BLACK);
canvas.drawText("Canvas Text Out", 10,200,Pnt);
}
}
}

public class Primitive2 extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyView vw = new MyView(this);
setContentView(vw);
}

protected class MyView extends View {
public MyView(Context context) {
super(context);
}

public void onDraw(Canvas canvas) {
Paint Pnt = new Paint();
//canvas.drawARGB(255,255,255,255);
//canvas.drawRGB(255,255,255);
//canvas.drawColor(0xffffffff);
Pnt.setColor(Color.WHITE);
canvas.drawPaint(Pnt);
RectF r=new RectF(10,10,100,100);
Pnt.setColor(Color.RED);
canvas.drawRoundRect(r,10,10,Pnt);
r.set(110,10,150,100);
canvas.drawOval(r,Pnt);
Pnt.setColor(Color.MAGENTA);
r.set(10,110,100,200);
canvas.drawArc(r,10,150,false,Pnt);
r.set(110,110,200,200);
canvas.drawArc(r,10,150,true,Pnt);
Pnt.setColor(Color.BLUE);
float[] pts={10,210,50,250,50,250,110,220};
canvas.drawLines(pts,Pnt);
Pnt.setColor(Color.BLACK);
float[] pts2={20,210,50,240,100,220};
canvas.drawPoints(pts2, Pnt);
}
}
}

<Paint>

그리기에 대한 속성 정보를 가지는 객체이다.

void setAntiAlias(boolean ) : 경계면을 부드럽게 처리해준다.

void setColor(int color) : 단색을 지정한다.

void setRGB(r, g, b) : 더 더양한 색상을 지정한다.

void setARGB(a, r, g, b) : RGB에 Alpha가 추가된 것이다. Alpha는 투명이냐 불투명이냐를 결정할 수 있다.

void setStrokeWidth(width) : 펜 굵기

void setStrokeCap(Paint.Cap cap) : 끝 모양

* BUTT : 지정한 좌표에서 선이 끝남

* ROUND : 둥근 모양으로 끝이 장식된다.

* SQUARE : 사각형 모양이되 지정된 좌표보다 조금 더 그어진다.

void setStrokeJoin(Paint.Join join) : 모서리처럼 선분이 만나 각지는 곳의 모양

* MITER : 모서리를 90도 각진 형태 (Default)

* BEVEL : 모서리가 깍인 형태

* ROUND : 둥근 형태

void setStrokeMiter(float miter) : 0 이상의 값으로 어느 정도 각도로 뾰족하게 할 것인가 (StrokeJoin의 소속)

void setStyle(Paint.Style style) : 외곽선과 내부 중 어느 쪽을 그릴 것인가

* FILL : 채우기만 하며 외곽선 X (Default)

* FILL_AND_STROKE : 채우기도 하고 외곽선도 그린다.

* STROKE : 채우지는 않고 외곽선만 그린다.

void set(Paint src) : 객체끼리 대입

void reset() : 초기화

cf) invalidate() : 새로 canvas를 그려준다.

마찬가지로 예제를 보며 실제적으로 어떻게 사용이 되는지 알아보자.

public class AntiAlias extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyView vw = new MyView(this);
setContentView(vw);
}

protected class MyView extends View {
public MyView(Context context) {
super(context);
}

public void onDraw(Canvas canvas) {
Paint Pnt = new Paint();
canvas.drawColor(Color.WHITE);
Pnt.setColor(Color.BLACK);
Pnt.setTextSize(30);

// 기본 출력
canvas.drawOval(new RectF(10,10,100,60), Pnt);
canvas.drawText("Text", 110, 40, Pnt);

// 안티 알리아스 적용
Pnt.setAntiAlias(true);
canvas.drawOval(new RectF(10,70,100,120), Pnt);
canvas.drawText("Text", 110, 100, Pnt);
}
}
}

AntiAlias는 색상 차가 뚜렷한 경계 부근에 중간색을 삽입, 도형이나 글꼴이 주변 배경과 부드럽게 어울리도록 하는 기법이다.

사용하게 되면 출력 품질은 좋아지지만, 중간 색상을 삽입하기 위한 연산을 해야 하므로 속도는 떨어진다.

디폴트를 false이므로 이 기법을 사용하려면, 반드시 true로 바꿔 주어야 한다.

public class PaintTest extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyView vw = new MyView(this);
setContentView(vw);
}

protected class MyView extends View {
public MyView(Context context) {
super(context);
}

public void onDraw(Canvas canvas) {
Paint Pnt = new Paint();
canvas.drawColor(Color.WHITE);

// 캡 모양 테스트
Pnt.setColor(Color.BLUE);
Pnt.setStrokeWidth(14);
canvas.drawLine(10,10,200,10,Pnt);
Pnt.setStrokeCap(Paint.Cap.ROUND);
canvas.drawLine(10,30,200,30,Pnt);
Pnt.setStrokeCap(Paint.Cap.SQUARE);
canvas.drawLine(10,50,200,50,Pnt);

// 조인 모양 테스트
Pnt.setColor(Color.BLACK);
Pnt.setStrokeWidth(15);
Pnt.setStyle(Paint.Style.STROKE);
Pnt.setStrokeJoin(Paint.Join.MITER);
canvas.drawRect(10,80,60,130,Pnt);
Pnt.setStrokeJoin(Paint.Join.BEVEL);
canvas.drawRect(80,80,130,130,Pnt);
Pnt.setStrokeJoin(Paint.Join.ROUND);
canvas.drawRect(150,80,200,130,Pnt);

// 스타일 테스트
Pnt.setStrokeWidth(5);
Pnt.setColor(Color.RED);
Pnt.setStyle(Paint.Style.FILL);
canvas.drawCircle(30,180,20,Pnt);
Pnt.setStyle(Paint.Style.STROKE);
canvas.drawCircle(80,180,20,Pnt);
Pnt.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawCircle(130,180,20,Pnt);
Pnt.setColor(Color.BLUE);
canvas.drawCircle(180,180,20,Pnt);
Pnt.setStyle(Paint.Style.FILL);
Pnt.setColor(Color.RED);
Pnt.setStyle(Paint.Style.STROKE);
canvas.drawCircle(180,180,20,Pnt);
}
}
}


반응형

댓글