|
안드로이드(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 |
