안드로이드(Android) SurfaceView 와 Thread 를 이용해 화면에 도형표현 하기 |
환경 : Eclipse Mars, Android 4.2.2 |
이전 예제에서 구현한 View 대신 게임등에 많이 쓰이는 SurfaceView 와 Thread 를 조합하여 클릭시 화면에 도형을 표시하는 샘플입니다. SurfaceView 는 View 보다 효율적입니다. 그래서 게임등을 만들 때 많이 사용하죠.
▼ 먼저 SurfaceView 와 인터페이스인 SurfaceHolder.Callback 두개를 상속받습니다. SurfaceHolder.Callback 는 SurfaceView 의 종료, 시작, 변경등의 이벤트를 감지하기 위해서 입니다. 그러니까 surfaceChanged(), surfaceCreated(), surfaceDestroyed() 는 무조건 구현해야 되는 것이죠. surfaceCreated() 에 thread 의 시작 코드가 있으며 surfaceDestroyed() 에는 thread 의 종료가 있습니다.
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { } public void surfaceCreated(SurfaceHolder holder) { thread.setRunning(true); thread.start(); } public void surfaceDestroyed(SurfaceHolder holder) { boolean retry = true; thread.setRunning(false); while (retry) { try { thread.join(); retry = false; } catch (InterruptedException e) { } } }
▼ 스크린 터치이벤트에서 도형을 그리기 위한 지금계산을 하게 됩니다. 이곳에 셋팅하는 순간에 drawing = true 로 바뀌므로 thread 의 run 에서 그림을 그리기 시작하게 되는 것입니다.
@Override public boolean onTouchEvent(MotionEvent event) { // return super.onTouchEvent(event); int action = event.getAction(); if (action == MotionEvent.ACTION_MOVE) { float x = event.getX(); float y = event.getY(); radius = (float) Math.sqrt(Math.pow(x - initX, 2) + Math.pow(y - initY, 2)); } else if (action == MotionEvent.ACTION_DOWN) { initX = event.getX(); initY = event.getY(); radius = 1; drawing = true; } else if (action == MotionEvent.ACTION_UP) { drawing = false; performClick(); } return true; }
▼ SurfacView 클래스에서 초기화하는 내용은 페인지 정보와 SurfaceThread 객체 생성시 SurfaceHolder 값등을 인수로 넘깁니다.
private void init() { getHolder().addCallback(this); thread = new SurfaceThread(getHolder(), this); setFocusable(true); // make sure we get key events paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(3); paint.setColor(Color.RED); }
▼ 이렇게 해서 만들어진 사용자 정의 SurfaceView 클래스는 다음과 같습니다.
public class EventSufaceView extends SurfaceView implements SurfaceHolder.Callback { private SurfaceThread thread; @Override public boolean onTouchEvent(MotionEvent event) { // return super.onTouchEvent(event); int action = event.getAction(); if (action == MotionEvent.ACTION_MOVE) { float x = event.getX(); float y = event.getY(); radius = (float) Math.sqrt(Math.pow(x - initX, 2) + Math.pow(y - initY, 2)); } else if (action == MotionEvent.ACTION_DOWN) { initX = event.getX(); initY = event.getY(); radius = 1; drawing = true; } else if (action == MotionEvent.ACTION_UP) { drawing = false; performClick(); } return true; } @Override public boolean performClick() { return super.performClick(); } public EventSufaceView(Context context) { super(context); init(); } public EventSufaceView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public EventSufaceView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { getHolder().addCallback(this); thread = new SurfaceThread(getHolder(), this); setFocusable(true); // make sure we get key events paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(3); paint.setColor(Color.RED); } public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { } public void surfaceCreated(SurfaceHolder holder) { thread.setRunning(true); thread.start(); } public void surfaceDestroyed(SurfaceHolder holder) { boolean retry = true; thread.setRunning(false); while (retry) { try { thread.join(); retry = false; } catch (InterruptedException e) { } } } }
▼ 두번째로 Thread 클래스를 상속받아 만든 SurfaceThread 입니다. 생성시 SurfaceHolder 와 EventSurfaceView 객체를 상속받습니다. 그리고 제일중요한 run 함수에서 실질적으로 도형을 그리게 됩니다. 그리기 전에 SurfaceHolder 의 lockCanvas 를 호출하여 잠금니다. 그리고 synchronized 이용하여 그림을 다 그리전에는 아무도 사용하지 못하도록 락을 걸어버립니다.
public class SurfaceThread extends Thread { private SurfaceHolder mThreadSurfaceHolder; private EventSufaceView mThreadSurfaceView; private boolean myThreadRun = false; private int dX = -100; public SurfaceThread(SurfaceHolder surfaceHolder, EventSufaceView surfaceView) { mThreadSurfaceHolder = surfaceHolder; mThreadSurfaceView = surfaceView; } public void setRunning(boolean b) { myThreadRun = b; } @Override public void run() { // super.run(); while (myThreadRun) { Canvas c = null; try { c = mThreadSurfaceHolder.lockCanvas(null); synchronized (mThreadSurfaceHolder) { if (drawing) { c.drawCircle(initX, initY, radius, paint); } } } finally { if (c != null) { mThreadSurfaceHolder.unlockCanvasAndPost(c); } } } } }
▼ 이렇게 Thread 클래스와 SurfacView 클래스를 만들었으면 메인 activity 에서 레이아웃 xml 대신 SurfacView 을 setContentView 의 인수로 넘깁니다.
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // setContentView(R.layout.activity_surface_view_draw); EventSufaceView mySurfaceView = new EventSufaceView(this); setContentView(mySurfaceView); }
'안드로이드 개발' 카테고리의 다른 글
안드로이드(Android) Activity 의 라이프사이클(LifeCycle) 알아보기위한 예제 (0) | 2014.12.16 |
---|---|
안드로이드(Android) SurfaceView 와 Thread 이용해 화면에서 움직이는 공구현 (0) | 2014.12.12 |
안드로이드(Android) SurfaceView 와 Thread 를 이용해여 사각형자동그리기 (0) | 2014.12.09 |
안드로이드(Android) getWidth() from the type Display is deprecated 수정 (0) | 2014.12.02 |
안드로이드(Android) 두개의 사용자정의 View 를 FrameLayout 으로 표현하기 (0) | 2014.11.30 |
안드로이드(Android) onDraw() 를 이용해 스크린터치로 원그리기 (1) | 2014.11.29 |
안드로이드(Android) 경고 - Custom view… overrides onTouchEvent but not performClick (0) | 2014.11.28 |
안드로이드(Android) onDraw 함수를 이용해 화면에 비트맵이미지, 도형 그리기 (0) | 2014.11.27 |