以前購入したAndroidプログラミングの本を参考に
SurfaceViewをいじってみたんだけど、Android4.xでは
動いたけど、5〜6辺りで全くうんともすんとも言わなかったので
色々調べてみたんだけど、なかなかうまく動くモノが無かった。
四苦八苦しながらやっと、サンプルにできるくらいは
出来たので記録。
(一部のAndroid6.0エミュレータではスワイプしたら落ちたけど
原因不明。試しに他の6.0で試してみたら落ちなかったので
Android6.0で落ちると言うことではなさそう。)
SurfaceViewとThread(ここではDrawThread)を
分けているサンプルなどもあるけど
分けて記述することも可能かと思いますが、
ここではSurfaceViewのファイルと一つにまとめて記述しています。
(AndroidStudio 3.1.4で作成)
必要javaファイルは2つ
*MainActivity.java
*SurfaceViewSample.java
■[MainActivity.java](任意の名称に変更して下さい)
(import部分は省略しています)
public class MainActivity extends AppCompatActivity implements SurfaceViewSample.Callback {
//クラス名を変更する場合はこちらも変更して下さい
private SurfaceViewSample surfaceViewSample;
private void onCreateProcess() {
//クラス名を変更する場合はこちらも変更して下さい
surfaceViewSample= new SurfaceViewSample(getApplicationContext());
surfaceViewSample.setCallback(this);
setContentView(surfaceViewSample);
}
private void onResumeProcess() {
surfaceViewSample.startDrawThread();
}
private void onPauseProcess() {
surfaceViewSample.stopDrawThread();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
onCreateProcess();
}
@Override
protected void onResume() {
super.onResume();
onResumeProcess();
}
@Override
protected void onPause() {
super.onPause();
onPauseProcess();
}
//Callback
public void onGameOver() {
//CallBack
//ゲームオーバーなど表示
//
}
}
※コールバックのonGameOver関数はSurfaceViewでゲームオーバーなどの
処理で使用できるように作っているだけで、サンプルでは使用していません。
【setContentView(surfaceViewSample)】の部分を
setContentView(new SurfaceViewSample(getApplicationContext()));
書き方も出来ますが、
Activity側で処理を止めるために(onPauseで止めています)
直接記述はしていません。
また、getApplicationContext()を引数で渡していますが、
thisなどのサンプルも見かけます。
どちらでもいいですが、一応渡すのはContextなので、
getApplicationContextの方がよろすぃみたいな説明を
されている方もいらっしゃいました。
■[SurfaceViewSample.java](任意の名称に変更して下さい)
※gameOver()関数は未使用ですが、ゲームオーバー等の処理で
MainActivityへのコールバックを作成しています。
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Handler;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class SurfaceViewSample extends SurfaceView implements SurfaceHolder.Callback {
private static final int FPS = 60;
DrawThread drawThread;
public interface Callback {
public void onGameOver();
}
private Callback callback;
public void setCallback(Callback callback) {
this.callback = callback;
}
private final Handler handler;
public SurfaceViewSample(Context context) {
super(context);
handler = new Handler();
getHolder().addCallback(this);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
startDrawThread();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
stopDrawThread();
}
public void processGameAll(Canvas canvas){
int disp_w=canvas.getWidth();
int disp_h=canvas.getHeight();
canvas.drawColor(0xffcccccc);//背景色
}
private class DrawThread extends Thread{
boolean isFinished;
@Override
public void run() {
//SurfaceHolder holder = getHolder();//途中でやり方を変更したためコメントアウトw
while (!isFinished) {
Canvas canvas;
//canvas = holder.lockCanvas();//上記コメントアウトの名残w
canvas = getHolder().lockCanvas();
if (canvas == null) {
stopDrawThread();
}else{
processGameAll(canvas);//メイン描画処理
//holder.unlockCanvasAndPost(canvas);//同じく上記コメントアウトの名残
//getHolder().unlockCanvasAndPost(canvas);
}
}
}
}
public void startDrawThread() {
stopDrawThread();
drawThread = new DrawThread();
drawThread.start();
}
public boolean stopDrawThread() {
if (drawThread == null) {
return false;
}
drawThread.isFinished = true;
drawThread = null;
return true;
}
//ここからMainActivityの関数(UI)へコールバック
private void gameOver() {
handler.post(new Runnable() {
@Override
public void run() {
callback.onGameOver();
}
});
}