まずはSurfaceView
何をblogネタにしようかと考えていて、思いついたのがSurfaceViewでした。ネットに転がっている情報をもとに以前もSurfaceViewを使ったCodeを書いてみたのですが、何となく久しぶりに復習をしようと思います。
今回は、Iconを上下左右に移動させながら描画するというものを作りました。
SurfaceView実装の要点を下記にまとめます。
それと実装してみての気づきを下記にまとめます。
で、実装はこんな感じになりました。
//SurfaceViewを拡張したclassを作成 //SurfaceHolderからのCallbackを受け取るSurfaceHolder.Callbackを実装(上記classに実装) public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback{ private static final String TAG = "MySurfaceView"; private static final String THREAD_NAME = "DrawingThread"; private MyDrawingThread mDrawingThread; private Context mContext; public MySurfaceView(Context context) { super(context); mContext = context; getHolder().addCallback(this); mDrawingThread = new MyDrawingThread(THREAD_NAME, this, mContext); } public MySurfaceView(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; getHolder().addCallback(this); mDrawingThread = new MyDrawingThread(THREAD_NAME, this, mContext); } //call back methodの実装 //SurfaceHolder classのcall back methodはUI threadから呼ばれる public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { } //call back methodの実装 //SurfaceHolder classのcall back methodはUI threadから呼ばれる public void surfaceCreated(SurfaceHolder holder) { Log.d(TAG, "Thread: " + Thread.currentThread().getName() + " call surfaceCreated()"); startDrawing(); } //call back methodの実装 //SurfaceHolder classのcall back methodはUI threadから呼ばれる public void surfaceDestroyed(SurfaceHolder holder) { Log.d(TAG, "Thread: " + Thread.currentThread().getName() + " call surfaceDestroyed()"); stopDrawing(); } private void startDrawing(){ mDrawingThread.startDrawing(); } private void stopDrawing(){ mDrawingThread.stopDrawing(); try{ mDrawingThread.join(); }catch(InterruptedException e){ } } private class MyDrawingThread extends Thread{ private static final int SPEED_X = 5; private static final int SPEED_Y = 5; private static final int LEFT = -1; private static final int RIGHT = 1; private static final int UP = -1; private static final int DOWN = 1; private volatile boolean mRun = false; private SurfaceView mSurfaceView; private Drawable mTargetImage; private int mSurfaceWidth; private int mSurfaceHeight; private Rect mBoundary = new Rect(); private int mTargetImageWidth; private int mTargetImageHeight; private int mDirectionX = RIGHT; private int mDirectionY = DOWN; private Paint mPaint = new Paint(); public MyDrawingThread(String name, SurfaceView surfaceView, Context context){ super(name); mSurfaceView = surfaceView; mTargetImage = context.getResources().getDrawable(R.drawable.icon); } private void initUiEnv(SurfaceView surfaceView){ mSurfaceWidth = surfaceView.getWidth(); mSurfaceHeight = surfaceView.getHeight(); mTargetImageWidth = mTargetImage.getIntrinsicWidth(); mTargetImageHeight = mTargetImage.getIntrinsicHeight(); mBoundary.left = 0; mBoundary.top = 0; mBoundary.right = mTargetImageWidth; mBoundary.bottom = mTargetImageHeight; mPaint.setColor(Color.WHITE); } public void startDrawing(){ mRun = true; initUiEnv(mSurfaceView); this.start(); } public void stopDrawing(){ mRun = false; } //描画Threadを実装 @Override public void run() { while(mRun){ //SurfaceHolder classのlockCanvas() methodを呼び出し、canvasを取得 Canvas canvas = mSurfaceView.getHolder().lockCanvas(); //取得したcanvasで描画処理 doDraw(canvas); //unlockCanvasAndPost() methodでcanvasを解放 mSurfaceView.getHolder().unlockCanvasAndPost(canvas); } } private void doDraw(Canvas canvas){ //描画するたび、canvasの塗りつぶしをしないと前の画像が残ったままになる canvas.drawColor(Color.BLACK); updateBoundary(); //Drawable classのdraw(Canvas)を呼ぶ前に必ずsetBoundary()を呼ぶ必要がある。 mTargetImage.setBounds(mBoundary); mTargetImage.draw(canvas); canvas.drawText("Left:" + mBoundary.left + " Right:" + mBoundary.right + " Top:" + mBoundary.top + " Bottom:" + mBoundary.bottom, 0, mSurfaceHeight - 20, mPaint); } private void updateBoundary(){ int newLeft = 0; int newTop = 0; newLeft = mBoundary.left + (SPEED_X * mDirectionX); if(mDirectionX == LEFT && newLeft <= 0){ mDirectionX = RIGHT; mBoundary.left = 0; mBoundary.right = mTargetImageWidth; }else if(mDirectionX == RIGHT && (newLeft + mTargetImageWidth) >= mSurfaceWidth){ mDirectionX = LEFT; mBoundary.right = mSurfaceWidth; mBoundary.left = mBoundary.right - mTargetImageWidth; }else{ mBoundary.left = newLeft; mBoundary.right = mBoundary.left + mTargetImageWidth; } newTop = mBoundary.top + (SPEED_Y * mDirectionY); if(mDirectionY == UP && newTop <= 0){ mDirectionY = DOWN; mBoundary.top = 0; mBoundary.bottom = mTargetImageHeight; }else if(mDirectionY == DOWN && (newTop + mTargetImageHeight) >= mSurfaceHeight){ mDirectionY = UP; mBoundary.bottom = mSurfaceHeight; mBoundary.top = mBoundary.bottom - mTargetImageHeight; }else{ mBoundary.top = newTop; mBoundary.bottom = mBoundary.top + mTargetImageHeight; } } } }
こんな感じでどうでしょう。始めてのpostだったので、まだ、未熟なとこがありますが、ご了承を。