프로그램은 크게 두가지 파일로 구성되어 있다.
ImgMove.java, GraphicsVies.java
ImgMove.java는 메인 엑티비티로, GraphicsView를 자신의 ContentView로 설정하게 된다.
그 소스코드는 다음과 같다.
===========================================
ImgMove.java
===========================================
package org.com;
import android.app.Activity;
import android.os.Bundle;
public class ImgMove extends Activity {
--* Called when the activity is first created. --
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new GraphicsView(this)); }
}
===========================================
여기서는 큰 무리가 없을거라고 생각한다.
그럼 이제 GraphicsView.java로 넘어가보자.
이 클래스는 SurfaceView 클래스를 extends 한 것이다.
보통 쓰레드를 내부클래스로 하나 만들어놓고 사용하는 경우가 많은데,
필자는 GraphicsView를 Runnable을 implements하여서 사용하였다.
(간단히 애니메이션 효과만 내고 싶은거라면 이게 더 편해보인다.)
그럼 이제 메인 코드로 넘어가보자.
=====================================================================
public class GraphicsView
extends SurfaceView implements SurfaceHolder.Callback, Runnable{
-- 여기서 주목할 점은 바로 SurfaceView를 extends했고, SurfaceHolder.Callback을 implements했다는 점!! * SurfaceHolder.Callback 은 surface의 변화에 대한 정보를 얻어오기 위해서 반드시 implements해야 하는 인터페이스이다. * SurfaceView 와 SurfaceHolder.Callback가 함께 사용될 경우, 잡혀있는 Surface는 created되는 사이사이마다만 사용 가능하다. * 그렇기 때문에 exclusive한 성질을 띄게 되고, synchronized(동기화)될 수 있는 것이다. 이 점에서 SurfaceView가 View에 비해 * 사용이 편리하다는 것인 듯 싶다. -- private Display display;
private int width;
private int height;
private Bitmap img;
private Bitmap src;
private Paint paint;
private SurfaceHolder holder; // surface의 동기화를 위해 화면을 잡아주고 있을 홀더 private Thread thread; // surface에 그려주는 작업을 해줄 thread int x = 0;
int y = 0;
public GraphicsView(Context context){
super(context);
display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
width = display.getWidth();
height = display.getHeight();
paint = new Paint();
src = BitmapFactory.decodeResource(this.getResources(), R.drawable.van);
holder = getHolder(); // 홀더를 만들고, holder.addCallback(this); // 홀더에 이 SurfaceView에 대한 콜백 인터페이스를 제공해준다.-- * 여기서 잠시, SurfaceHolder가 뭐하는 놈인지는 알고 넘어가는 편이 속시원할 것 같다. * SurfaceHolder란, 화면의 디스플레이에 대한 권한을 가지고 있는 객체에 대한 인터페이스이다. * 그렇기 때문에, SurfaceHolder는 프로그래머로 하여금 surface size, format를 제어할 수 있게 해주고, * surface의 픽셀을 수정하고, 변화를 모니터할 수 있게 해준다. -- setFocusable(true);
}
-- * SurfaceHolder.Callback을 implements했다면 반드시 surfaceCreated, surfaceChanged, surfaceDestroyed를 구현해주어야 한다. * 안그러면 에러가 날테니까?? --public void surfaceCreated(SurfaceHolder holder){ // surface가 첫번째로 생성되자마자 바로 호출되는 메소드 thread = new Thread(this); // 나중에 밑에서 구현될 스레드를 시작하는 지점. thread.start();
}
public void surfaceChanged(SurfaceHolder holder, int a, int b, int c){// surface에 포멧이나 사이즈 등 어떠한 변화가 생기면 바로 호출되는 메소드 }
public void surfaceDestroyed(SurfaceHolder holder){// surface가 종료되기 직전 바로 호출되는 메소드 }
-- * run()와 doDraw()는 이제 그리기 작업에 필요한 작업들만 해주면 된다. --public void run(){
int flag = 0;
int flagCnt = 0;
while(true){
Canvas c = null; // 그림을 그릴 캔버스 try{
c = holder.lockCanvas(null); // 해당 캔버스에 이제 그림그리기를 시작하기 위해 lock을 얻어왔다. synchronized(holder){ // 동기화를 위해 반드시 필요한 synchronized. holder가 잡고 있는 surface에 대해서 비동기식이 아닌 동기식으로 업데이트가 진행되도록 해준다. if(flag == 0 && y >= 0 && y < src.getHeight() - height){
doDraw(c); // 캔버스에 그림을 그립시다아~~ x++;
y++;
flagCnt++;
if(flagCnt == src.getHeight() - height){
flag = 1;
}
}else{
doDraw(c);
x--;
y--;
flagCnt--;
if(flagCnt == 0){
flag = 0;
}
}
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(c != null){
holder.unlockCanvasAndPost(c); // surface의 픽셀들에 대한 수정작업을 마무리 한다. unlockCanvasAndPost가 호출되고 난 뒤에 수정된 픽셀들이 스크린에 반영이 된다. }
}
}
}
public void doDraw(Canvas c){
img = Bitmap.createBitmap(src, x, y, width, height);
c.drawBitmap(img, 0, 0, paint);
}
이 소스코드가 도움이 되었으면 한다.
다음은 외부 블로그에서 SurfaceView를 사용하기 위해 필요한 과정들에 대해 정리해져 있는 내용을 펌해온 것이다.
소스를 보고 공부를 했으면, 그를 바탕으로 마무리로 정리해본다는 마음으로 읽어보면 도움이 될 것이다.
출처: http://samse.tistory.com/entry/Android-Graphics-1
On a SurfaceView-ML:NAMESPACE PREFIX = O />
SurfaceView는 View구 조내에서 surface를 그리는 목적으로 고안된 특별한 subclass이 다. 목 적은 애 플리케이션의 두 번째 스 레드에서 별 도로 그 려서 시 스템의 View구 조에서 그 리기 타 임까지 기 다리지 않 도록 하 게 하 기 위 해 고 안되었다. 두 번째 thread에 서 참 조하는 SurfaceView는 자신만의 Canvas에 그리기를 수행한다.
1. SurfaceView를 extend한 새로운 클래스를 선언한다.
2. SurfaceHolder.callback을 implement한 다. 이 콜백 subclass는 하부의 Surface에 대한 정보와 함께 통지하는데 생성, 변 경 또 는 제 거되었을 때 통지하게 된다. 이 이벤트를 통해서 언제 그리기를 시작해야 하는지 알수 있고, 새 로운 surface속 성에 맞 게 조 정하고, 언 제 그 리기를 종 료하고 테 스트를 죽 일지등을 알 수 있 다. SurfaceView에 서 두 번째 Thread를 정의하여 모든 drawing procedure를 수행하는 것이 좋다.
3. SurfaceHolder를 생성한다. Surface 객 체를 직 접 처 리하지 않 고 SurfaceHolder를 통해서 하는 것이 좋다. SurfaceView가 초기화되면 getHolder()로 SurfaceHolder를 얻는다.
4. addCallback()을 호출하여 SurfaceHolder.Callback으 로부터 이 벤트를 수 신하도록 한 다. SurfaceHolder.Callback을 SurfaceView클 래스내에서 override하 여 addCallback()한 다.
5. 두번째 thread에 서 Surface에 Canvas를 draw하 려면 반 드시 SurfaceHandler를 전달해야 하며 lockCanvas()로 canvas를 얻어야 한다. 이 렇게 해 야 SurfaceHolder가 제공해준 Canvas를 얻게 된다.
6. Canvas에 drawing을 한번 수행하면 canvas object를 인자로 unlockCanvasAndPost()를 호출해야 한다. lockCanvad()와 unlockCanvasAndPost()는 draw시 반드시 쌍으로 호출되어야 한다.
Note: 한 번 canvas가 얻어져서 사용되면 그 canvas는 이전상태를 유지한다. 따 라서 적 절하게 animate를 하려면 surface전 체를 re-paint해 야 한 다. 이 경우 drawColor()난 drawBitmap()등 으로 background를 갱신하면 된다. 그 렇게 하 지 않 으면 이 전 이 미지가 표 시될 것 이다.
최근 덧글