post list

2013년 1월 24일

[Android] Canvas에 도형 그리기 Example


Android Circle, Rectangle, Triangle 그리기



  1. 화면 구성 (View를 상속받은 Class를 이용)


 위의 화면은 크게 2가지로 나눠진다. 첫번째는 하단에 존재하는 버튼 4개를 가지고 있는 레이아웃, 2번째는 View를 상속받은 클래스를 레아아웃 XML상에서 구현하여 Canvas로 활용되는 레이아웃이다.
 Canvas로 활용되는 레이아웃은 View를 상속해서 Paint, Path, Canvas 객체를 활용해서 Canvas View를 구현할 수 있었다


2. View를 상속받은 클래스를 XML 상에서 구현하는 과정

 예를 들어 Java 파일에서 Custom View를 위한 클래스를 다음과 같이 만들었다고 하자. 

class CanvasView extends View


 이렇게 View를 상속받게 되면 생성자를 생성하게 되는데 View에는 필요에 따른 3종류의 생성자가 View에 구현되어 있다.
 (참고 사이트 : Custom Android View : Definition)

 첫번째 생성자는 다음과 같다.

public CanvasView(Context context) {
super(context);
}

 이 생성자는 코드상에서 직접 CanvasView를 구현할 때 사용되는 생성자로 Parameter로 오직 Context만은 요구한다. 하지만 XML 상에서 사용하기 위해서는 이 생성자만 구현해서는 안된다.

 두번째 생성자는 다음과 같다.
public CanvasView(Context context, AttributeSet attribs) {
super(context, attribs);
}

 이 생성자는 XML상에서 CanvasView를 구현하기 위해서 사용된다. 즉, XML상에서 Custom View를 사용하기 위해서는 반드시 구현해줘야 한다. 이 생성자는 Parameter로 Context와 더불어 AttributeSet를 요구한다.
 여기서 AttributeSet 이란 Attribute들의 집합체로 Resource File (XML파일)에서 사용되는 속성들을 의미한다.
 (참고 사이트 :  Google Android Developer : AttributeSet)

 세번째 생성자는 다음과 같다.
public CanvasView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
} 

두번째 생성자와 마찬가지로 Resource File에서 사용되는 생성자다. 두번째와 다른 점은 Parameter로 int defStyle을 요구한다는 점인데 만약 defStyle이 0일 경우에는 현재 어떤 Theme도 적용시키지 않게 된다. 즉, defStyle에 적용되는 Value에 따라 theme를 변경시킬 수 있게 된다.

 기본적으로 이러한 생성자들을 구현하고 나면 XML 상에서 Custom View를 적용시킬 수 있게 된다. 적용 방법은 패키지 패쓰와 클래스 이름을 함께 XML상에서 적어주면 된다. 

 <com.example.androidtest.CanvasView
        android:id="@+id/canvasView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/> 

여기서 com.example.androidtest는 패키지 Path를 의미하고 CanvasView는 View를 상속받은 클래스의 이름이다.


3. 필요한 메서드를 Override 하기

 상속받은 View에는 중요한 여러가지 메서드가 많이 만들어져 있다. 우리는 이 메서드를 Custom View 클래스에서 오버라이드하여 사용이 가능하다. View에 구현되어 있는 메서드의 종류중 대표적인 메서드는 다음과 같다.

public boolean onKeyDown(int keyCode, KeyEvent keyEvent);
public boolean onKeyUp(int keyCode, KeyEvent keyEvent);
public boolean onTrackballEvent(MotionEvent event);
public boolean onTouchEvent(MotionEvent event);
protected void onMeasure(int wMeasureSpec, int hMeasureSpec);
protected void onDraw(Canvas canvas)


메서드 이름만 봐도 직관적으로 어떤 기능을 하는 메서드인지 알 수 있기 때문에 몇개의 메서드만 짚고 넘아가자.

public boolean onTouchEvent(MotionEvent event);

 위의 메서드는 화면에 터치를 했을때마다 호출되는 메서드다. 주어지는 인수로 MotionEvent가 들어온다. 


protected void onMeasure(int wMeasureSpec, int hMeasureSpec);

 위의 메서드는 Parameter로 부모View로 부터 결정된 화면 치수를 인수로 넘겨받는다. 이 메서드는 본 프로그램에서 사용하지 않았다. 자세한 것은 아래의 사이트에서 확인할 수 있다.


protected void onDraw(Canvas canvas);

 위의 메서드는 실제로 View에 그림을 그릴 수 있도록 해주는 메서드다. 인수로 Canvas가 들어오는데 이 Canvas에 Paint와 Path를 이용해서 화면을 구성할 수 있도록 도와준다.

4. Paint

 Paint는 Canvas에 무언가를 그리려고 할 때 사용되는 객체다. 이 녀석은 그림을 그릴 때 사용되는 붓의 종류와 같은 역할을 한다. 예를 들어 다음과 같다.

Paint pnt = new Paint();
pnt.setAntiAlias(true);
pnt.setColor(color);
pnt.setStrokeWidth(width);
pnt.setStyle(Paint.Style.STROKE);

 setAntiAlias(boolean)는 그림이 화면에 표현될 때 더욱 자연스러워 보이게 한다. 왜냐하면 그려지는 그림 주변에 옅은 색상을 더 뿌려서 화면에 표현되게 하기 때문이다.
 setStyle(Paint.Style)은 인수에 따라 그려지는 그림이 달라진다. 무슨 말인가 하면 FILL값으로 Parameter를 넘겨주게 되면 동그라미를 그렸을 때 그 안이 꽉 차게 된다. 그러나 STROKE를 넘겨주게 되면 동그라미 안은 비어있게된다.
 나머지 메서드는 직관적인 이름이기 때문에 생략한다. (Paint에 대한 더욱 자세한 정보는 검색을 통해서 알 수 있을 것이다)


5. Path

 Path는 도형과 관련된 객체로 실제로 그림을 그리는데 중요한 역할을 한다. 원, 사각형, 호, 타원등을 그리는 메서드를 기본적으로 제공해준다. 
 moveTo(int x, int y) 메서드를 사용해서 화면상에 특정 좌표로 이동하는 것도 가능하며 lineTo(int x, int y) 메서드를 이용해 직선을 긋는것도 가능하다. 특히 rLineTo(int x, int y)의 경우에는 상대좌표를 적용하여 그릴 수 있기 때문에 특히 편리하다.
 혹은 Path의 reset()메서드를 사용한 다음 캔버스에 그려주게 되면 캔버스를 깨끗하게 지울 수 있기도 하다.


6. Canvas

 View의 onDraw(Canvas)는 인수로 Canvas를 넘겨준다. 이 Canvas가 화면 상에 보이는 실제 화면이다. Canvas.drawColor()를 이용하면 기본 화면 색상을 정할 수도 있으면 Canvas.drawPath를 이용해 미리 Path를 만들어저 Canvas에 적용할 수도 있다.



7. 동그라미, 세모, 네모 구현

 먼저 Paint를 생성하고 설정을 해준다. 왜냐하면 본 프로그램에서는 색상나 굵기 등을 변경하지 않기 때문이다. 만약 이러한 기능을 설정하고 싶다면 따로 기능을 추가하여 Paint 값을 변경시켜주면 된다. 여기서는 Paint의 색상을 Black, 두께를 10으로 맞춰줬다.

 동그라미, 세모, 네모 버튼을 눌리게 되면 CanvasView에게 Int값을 넘겨주도록 한다. 이 Int 값은 CanvasView에게 사용자가 어떤 것을 그리고 싶은지에 대한 정보를 넘겨주는 것과 같다.

 버튼을 선택한 후에 화면을 터치하게 되면 CanvasView의 onTouchEvent(MotionEvent) 메서드가 호출된다. onTouchEvent 메서드에서 MotionEvent.getAction()과 Switch문을 이용해서 사용자가 손을 뗏을 경우에 그림이 그려지도록 했다. 또한 MotionEvent는 사용자가 터치한 좌표값을 넘겨주기 때문에 이 값은 CanvasView 자체 멤버로 저장되도록 했다. 

 그려지는 그림은 사용자가 미리 선택한 버튼에 따라 달라진다. 동그라미의 경우 Path의 addCircle메서드를 호출하고 네모의 경우 Path의  addRect메서드를 호출한다. 그러나 세모의 경우에는 Path에서 기본적으로 지원이 되지 않는다. 그래서 정삼각형을 기본으로 해서 Math의 Sin, Cos 함수를 이용해 그린다. 특히 여기서 상대좌표를 적용할 수 있는 Path의 rLineTo메서드가 유용하게 사용된다. 이 메서드를 이용해 삼각형을 좌표를 계산하여 그려준다. 
 그런데 문제는 딱 삼각형을 그리게 되면 마지막 끝점이 다음과 같이 어긋나게 된다.



그래서 해결한 방법은 한번더 rLineTo를 이용해 선을 그어주는 것이다. 그렇게 하면 다음과 같이 깔끔한 삼각형이 만들어진다.




소스 코드는 프로젝트 자체를 압축해서 아래의 링크에 걸어놨으니 참고하세요.

Project Source Download







댓글 없음:

댓글 쓰기