[출처 : http://blog.naver.com/khs7515?Redirect=Log&logNo=20156449363 ]
-----------------------------------------------------------------------------------------------------
이분 블로그를 앞으로 많이 이용할듯하다. 은근히 섬세한 설명에 이유까지 잘설명됨
--------------------------------------------------------------------------------------------------
# SurfaceView는 View보다 구조상 복잡하긴 하지만 성능이 더 좋기 때문에 게임, 또는 렌즈에 비치는 모습을 휴대폰 화면에
빠르게 보여주기 위해서 사용된다.
# 예를 들어 설명하자면 100개의 물건을 옮기는데 View는 1개씩 옮겨다 놓지만 SurfaceView는 100개를 실어서 한번에 옮기는 형태랄까
# 화면이 갱신되는 동안 모든 객체의 정보를 담아두었다가 보여주는 것을 반복한다.
# 다음 예제는 임의로 움직이는 공을 무한하게 만들어서 SurfaceView가 어느 정도까지 버틸 수 있는가를 실험해보는 것이다.
# 모토로이 기준 1,500개정도가 되면 속도가 현저히 늦어지는 것을 볼 수 있었다.
******************************************** MainActivity.java *************************************************
package test.surfaceview.test2;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new GameView(this));
}
}
******************************************** GameView.java *************************************************
package test.surfaceview.test2;
import android.content.Context;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/*
* [[ 빠른 그래픽 처리를 위한 SurfaceView 사용하기 ]]
* 1. surfaceview 클래스를 상속받는다.
* 2. surfaceHolder.callback인터페이스를 구현한다.
* 3. 인터페이스의 추상메소드를 오버라이딩 한다.
* 4. 백그라운드에서 작업할 스레드를 만들어서 SurfaceHolder 객체를 넘겨준다.
* 5. 스레드에서는 surfaceholder 객체를 이용해서 그래픽 작업한다.
* 6.
*/
//생성자 1개짜리는 View만 쓸때. 2개짜리는 다른것도 쓸때
public class GameView extends SurfaceView implements SurfaceHolder.Callback{
Context context;
SurfaceHolder sHolder;
GameThread gameThread;
int width, height; //surfaceView가 차지하고 있는 실제 공간의 크기
public GameView(Context context) {
super(context);
this.context = context;
init(); //초기화
}
//초기화하는 메소드
public void init(){
//SurfaceHolder객체 얻어오기
sHolder = getHolder(); //surface가 holder를 가지고 있기 때문에 그냥 getholder하면 된다.
//콜백 객체 등록
sHolder.addCallback(this);
//스레드 객체 생성
gameThread = new GameThread(context, sHolder);
}
//뷰화면의 크기가 변화가 생겼을 때 호출되는 메소드(화면의 폭과 높이 정보가 인자로 들어온다.)
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
Log.e("*********", "surfaceChanged()");
this.width = width;
this.height = height;
gameThread.setScreenSize(width, height);
Log.e("*********", "width:"+width+" / height:"+height);
}
//뷰가 처음 만들어 질 때 호출되는 메소드
public void surfaceCreated(SurfaceHolder holder) {
Log.e("*********", "surfaceCreated()");
try {
//스레드를 시작시킨다.
gameThread.start();
} catch (Exception e) {
Log.e("********", "스레드 시작 시 에러 발생! 스레드를 다시 생성");
//에러 발생하면 재시작하기
restartThread();
}
}
//뷰가 파괴될 때 호출되는 메소드
public void surfaceDestroyed(SurfaceHolder holder) {
Log.e("*********", "surfaceDestroyed()");
//에러 없이 스레드를 안전하게 종료하기 위해
boolean tryJoin = true;
gameThread.stopForever(); //종료시 에러를 잡기 위한 핵심!
while(tryJoin){//join이 성공할때까지
try{
gameThread.join();
tryJoin=false;
}catch (Exception e) {
}
}
}
//스레드를 재시작하는 메소드
public void restartThread(){
//스레드 정지
gameThread.stopForever();
//스레드 비우고
gameThread = null;
//객체 다시 생성
gameThread = new GameThread(context, sHolder);
//화면의 폭과 높이 전달
gameThread.setScreenSize(width, height);
//스레드 시작
gameThread.start();
}
//스레드 일시정지하는 메소드
public void pauseThread(){
gameThread.pauseNResume(true);
}//일시 정지된 스레드를 재시작하는 메소드
public void resumeThread(){
gameThread.pauseNResume(false);
}
}
******************************************** GameThread.java *************************************************
package test.surfaceview.test2;
import java.util.ArrayList;
import java.util.Random;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.util.Log;
import android.view.SurfaceHolder;
public class GameThread extends Thread{
Context context;
SurfaceHolder sHolder; //핵심개체이며 화면의 구성에 대한 정보를 모두 가지고 있다.
boolean isRun = true;
boolean isWait = false;
Canvas canvas; //화면에 무언가를 그리기 위한 캔바스 객체(sHolder가 가지고 있다)
int width, height;
//랜덤한 수를 발생시키기 위해
Random ran = new Random();
//색을 저장할 배열
Paint[] paint = new Paint[5];
//원 객체를 저장할 배열
ArrayList<Circle> list = new ArrayList<Circle>();
//생성자
public GameThread(Context context, SurfaceHolder sHolder){
this.context = context;
this.sHolder = sHolder;
//원을 그릴 색 만들어서 배열에 저장하기
initPaint();
}
//화면의 폭과 높이를 전달바든ㄴ다
public void setScreenSize(int width, int height){
this.width = width;
this.height = height;
Log.e("*********", "width:"+width+" / height:"+height);
}
//빠르게 돌아가는 스레드에서 메소드를 실행하거나 멤버필드가 교환되는 작업을 하면
//스레드 작업이 깨질수있기 때문에 동기화가 필요하다.
//스레드의 일시정지 혹은 재시작 하는 메소드
public void pauseNResume(boolean isWait){
synchronized (this) {
this.isWait = isWait;
notify();
}
}
//스레드 완전 정지하는 메소드
public void stopForever(){
synchronized (this) {
this.isRun = isRun;
notify();
}
}
//스레드 본체인 run메소드에서 그리는 작업을 해준다.
@Override
public void run() {
while(isRun){ //isRun의 초기 값이 true 이므로 기본적으로 무한 루프이다.
//Canvas객체 얻어오기
canvas = sHolder.lockCanvas();//화면정보를 다 담을때까지 화면을 잠궈놓는다.
//화면에 그리는 작업을 한다.
try{
//동기화 블럭에서 작업을 해야한다.
synchronized (sHolder) { //그리기위한 모든 작업을 하는 곳
//canvas 객체를 이용해서 반복적인 그리기 작업을 한다.
canvas.drawColor(Color.WHITE);
//반복문을 돌면서 원을 그린다
for(int i=0 ; i < list.size() ; i++){
//해당 인덱스의 원객체를 얻어온다.
Circle cir = list.get(i);
canvas.drawCircle(cir.x, cir.y, //원의 위치
5, //반지름
paint[cir.color]); //원의 색
}
canvas.drawText("공의수:"+list.size(), 0, 20, paint[0]);
//원 만들기(10번 돌때 한번의 확률로 원을 만들기 위해)
//if(ran.nextInt(5)==0)
makeCircle();
//원 움직이기
moveCircle();
}
}finally{
if(canvas!=null)//실제로 화면을 그리는 곳(while을 돌면서 화면을 덧그리기 때문에 invalidate가 필요하지 않다)
//잠근 화면을 풀고 작업한canvas 객체를 전달해서 화면을 그린다.
sHolder.unlockCanvasAndPost(canvas);
}
//스레드를 일시 정지 하기 위해
if(isWait){ //isWait의 초기값은 false 이다
try {
synchronized (this) {
wait(); //notify할때까지 기다린다.(일시정지)
}
} catch (Exception e) {}
}//if
}//while
}//run
//페인트 객체를 생성해서 배열에 담는 메소드
public void initPaint(){
//paint 객체 생성해서 배열에 담기
Paint p1 = new Paint();
p1.setColor(Color.DKGRAY);
p1.setStyle(Style.FILL); // 원을 채우도록
Paint p2 = new Paint();
p2.setColor(Color.RED);
p2.setStyle(Style.FILL); // 원을 채우도록
Paint p3 = new Paint();
p3.setColor(Color.GREEN);
p3.setStyle(Style.FILL); // 원을 채우도록
Paint p4 = new Paint();
p4.setColor(Color.BLUE);
p4.setStyle(Style.FILL); // 원을 채우도록
Paint p5 = new Paint();
p5.setColor(Color.YELLOW);
p5.setStyle(Style.FILL); // 원을 채우도록
//배열에 담는다.
paint[0]=p1;
paint[1]=p2;
paint[2]=p3;
paint[3]=p4;
paint[4]=p5;
}
//circle 객체를 만드는 메소드
public void makeCircle(){
//원의 위치
int x = ran.nextInt(width); //화면의 폭 안의 랜덤한 x지점
int y = ran.nextInt(height); //화면의 높이 안의 랜덤한 y지점
//원의 초기 속도
int speedX = ran.nextInt(10)-5;
int speedY = ran.nextInt(10)-5;
//원의 색
int color = ran.nextInt(5);
//Circle 객체를 생성해서 배열에 담는다
Circle cir = new Circle(x, y, color, speedX, speedY);
list.add(cir);
}
//circle객체를 움직이는 메소드
public void moveCircle(){
//반복문 돌면서 원 객체 움직이기
for(Circle cir : list){
cir.x += cir.speedX;
cir.y += cir.speedY;
//화면을 벗어나지 못하도록 처리
if(cir.x <= 0 || cir.x >= width){
//속도의 부호를 바꾸어서 반대방향으로 움직인다.
cir.speedX *= -1;
//바뀐 속도로 한번 움직여 준다.
cir.x += cir.speedX;
}
if(cir.y <= 0 || cir.y >= height){
//속도의 부호를 바꾸어서 반대방향으로 움직인다.
cir.speedY *= -1;
//바뀐 속도로 한번 움직여 준다.
cir.y += cir.speedY;
}
}
}
}
******************************************** Circle.java *************************************************
package test.surfaceview.test2;
public class Circle {
int x, y;
int color;
int speedX, speedY; //속도
public Circle(int x, int y, int color, int speedX, int speedY) {
super();
this.x = x;
this.y = y;
this.color = color;
this.speedX = speedX;
this.speedY = speedY;
}
}
[출처] [안드로이드] SurfaceView를 이용한 움직이는 공 만들기|작성자 지탄
'Android_Programma' 카테고리의 다른 글
[android] 영상처리 NDK 단에서 처리하여 리턴하기 (Image Processing in NDK) (5) | 2012.07.10 |
---|---|
[android] [펌] SurfaceView 의 이해와 CameraView 띄우기 (0) | 2012.07.09 |
JNI 환경 구축하기 (0) | 2012.07.06 |
[JAVA] 비동기 소켓 클라이언트 (async socket client in JAVA) (0) | 2012.06.14 |
android 에 phone gap 설치하기 (펌) (0) | 2012.03.21 |