티스토리 뷰

SurfaceView는 View를 상속받는 클래스이다.

일반 View는 onDraw 메소드를 시스템에서 자동으로 호출해줌으로써 화면을 그린다. 그래서 화면에 늦게 그려질 수도 있다.

SurfaceView는 그리기를 시스템에 맡기는 것이 아니라 스레드를 이용해 강제로 화면에 그림으로써 원하는 시점에 바로 화면에 그릴 수 있다.

그래서 SurfaceView는 애니메이션이나 동영상과 같이 연산처리가 많이 필요한 뷰를 위해 사용된다.

SurfaceView는 더블 버퍼링 기법을 이용하여 SurfaceHolder가 Surface에 미리 그리고 이 Surface가 SurfaceView에 반영되는 방식이다.

SurfaceView는 자기 영역 부분의 Window를 뚫어서(punch) 자신이 보여지게끔 하고 Window와 View가 블렌딩되어 화면에 보여지게 된다.

 

 

SurfaceView로부터 상속받을 경우 디폴트로 구현해야 할 메소드가 있다.

  • public void onDraw (Canvas canvas) : 화면을 그린다.
  • public void surfaceChanged() : 뷰가 변경될 때 호출된다.
  • public void surfaceCreated() : 뷰가 생성될 때 호출된다.
  • public void surfaceDestroyed() : 뷰가 종료될 때 호출된다.

 

예제

안드로이드 캐릭터 이미지가 화면에 보이는 간단한 예제이다.

 

MySurfaceView.java

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
    Context mContext;
    SurfaceHolder mHolder;
    RenderingThread mRThread;
 
    public MySurfaceView(Context context) {
        super(context);
        mContext = context;
        mHolder = getHolder();
        mHolder.addCallback(this);
        mRThread = new MySurfaceView.RenderingThread();
    }
 
    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        // Surface가 만들어질 때 호출됨
        mRThread.start();
    }
 
    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
        // Surface가 변경될 때 호출됨
    }
 
    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        // Surface가 종료될 때 호출됨
        try {
            mRThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
 
 
    class RenderingThread extends Thread {
        Bitmap img_android;
        public RenderingThread() {
            Log.d("RenderingThread", "RenderingThread()");
            img_android = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.android);
        }
 
        public void run() {
            Log.d("RenderingThread", "run()");
            Canvas canvas = null;
            while (true) {
                canvas = mHolder.lockCanvas();
                try {
                    synchronized (mHolder) {
                        canvas.drawBitmap(img_android, 0, 0, null);
                    }
                } finally {
                    if (canvas == null) return;
                    mHolder.unlockCanvasAndPost(canvas);
               }
            }
        }
    } // RenderingThread
}
 

 

1 : SurfaceView를 상속받는 MySurfaceView 클래스를 정의한다. 그리고 실제 작업 수행은 SurfaceHolder.Callback이 수행하므로 implements 로 callback들의 구현한다.

6~12 : SurfaceHolder에 addCallback()으로 MySurfaceView 클래스에 있는 callback을 등록한다. 그리고 View에 rendering을 할 Thread를 생성한다.

14~28 : @Override라고 annotation이 적혀있는 surfaceCreated(), surfaceChanged(), surfaceDestroyed() 등이 callback을 재정의했다. SurfaceHolder.Callback을 implements 할시 반드시 override 구현해야할 callback 함수들이다. 함수의 이름에서 알 수 있듯이 surfaceCreated()는 surface가 생성되었을 때 호출되는 함수이고, surfaceChanged()는 surface가 변경되었을 때 호출되며, surfaceDestroyed()는 surface가 destroy될 때 호출된다. 따라서 17번 줄을 보면 surfaceCreated() 함수가 호출되면, 즉 surface가 생성되면 RenderingThread를 시작시킨다.

31 : Rendering 작업을 수행할 thread를 정의하고 있다. SurfaceHolder의 callback 에서 이 thread를 사용해서 작업을 수행한다.

33~36 : RenderingThread의 생성자가 불릴 때 미리 res/drawable/android.png 파일을 Bitmap으로 만들어놓는다. 이후에 canvas.drawBitmap() 메소드에 사용된다.

38~51 : 17번 줄에서 처럼 surfaceCreated() 함수가 호출되면 RenderingThread object에 start()를 호출하는데, start() 내부에서 바로 이 run() 메소드가 불리게 된다. 42번 줄에서 SurfaceHolder로부터 lockCanvas()로 Canvas object를 얻고, 44번 줄에서 SurfaceHolder에 대해 동기화를 유지하면서 45번 줄에서 Canvas에 위에서 미리 생성해둔 Bitmap을 버퍼에 그린다. 모두 그리고 나면 49번 줄에서 canvas의 내용을 view에 전송한다.

MainActivity.java

public class MainActivity extends AppCompatActivity {
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new MySurfaceView(this));
    }
}

3 : MainActivity에 위에서 정의한 MySurfaceView 오브젝트를 생성하여 content view로 설정한다.

댓글

파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음



Total
Today
Yesterday
최근에 달린 댓글