前回の「タッチ入力とマルチスレッド」の記事では、SurfaceViewを使ったプログラムで発生する、マルチスレッドの問題を取り上げました。
・タッチ入力とマルチスレッド(1)
・タッチ入力とマルチスレッド(2)
前回の記事のサンプルコードは、ほぼそのままSurfaceViewを使ったゲームの基礎として使用できます。
ただまだ一つ問題があります。
onPause、onResumeに対応していないため、ホームボタンでアプリがバックグラウンドに回っても、ゲームが停止しません。
今回はその対応を行った、最小限のSurfaceViewプログラムを紹介します。
では少し長いですがプログラムの全文です。
public class CActivityMain extends Activity implements Runnable, View.OnTouchListener { static public final class CTouchEvent { public int mAction; public float mX; public float mY; CTouchEvent(MotionEvent iEvent) { mAction = iEvent.getAction(); mX = iEvent.getX(); mY = iEvent.getY(); } } private List<CTouchEvent> mEventListMain = new ArrayList<CTouchEvent>(); private List<CTouchEvent> mEventListUI = new ArrayList<CTouchEvent>(); private SurfaceView mView; private Thread mThread; private volatile boolean mThreadRun; private float mX; private float mY; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); mView = (SurfaceView)findViewById(R.id.surfaceView1); mView.setOnTouchListener(this); } @Override protected void onResume() { super.onResume(); mThreadRun = true; mThread = new Thread(this); mThread.start(); } @Override protected void onPause() { super.onPause(); mThreadRun = false; while(true) { try { mThread.join(); break; } catch (InterruptedException iException) { } } mThread = null; } @Override public void run() { SurfaceHolder aHolder = mView.getHolder(); Paint aPaint = new Paint(); aPaint.setColor(Color.RED); while(mThreadRun) { if(aHolder.getSurface().isValid()) { synchronized (this) { mEventListMain.clear(); List<CTouchEvent> aTemporary = mEventListMain; mEventListMain = mEventListUI; mEventListUI = aTemporary; } for(CTouchEvent aTouchEvent : mEventListMain) { if(aTouchEvent.mAction == MotionEvent.ACTION_DOWN) { mX = aTouchEvent.mX; mY = aTouchEvent.mY; } } Canvas aCanvas = aHolder.lockCanvas(); aCanvas.drawColor(Color.WHITE); aCanvas.drawCircle(mX, mY, 50.0f, aPaint); aHolder.unlockCanvasAndPost(aCanvas); } } } @Override public boolean onTouch(View iView, MotionEvent iEvent) { synchronized (this) { mEventListUI.add(new CTouchEvent(iEvent)); } return true; } }
ほとんどは「タッチ入力とマルチスレッド」の記事のものと同じです。
今回は変更点のみ解説します。
まずonPause、onResumeでどのような処理を行わなくてはいけないかを説明します。
ホームボタンが押されるとonPauseが呼ばれます。
onPauseではゲーム処理を停止させる必要がありますが、停止といっても要はスレッドを終了させてしまえばよいわけです。
そしてアプリに復帰した際にonResumeが呼ばれるので、そこでもう一度スレッドを開始すればゲームは再開します。
ゲームに必要なデータはスレッドが持っているわけではないため、スレッドが終了しても、ゲーム中の値が消えたりすることはありません。
ということで、まずはonResumeです。
@Override protected void onResume() { super.onResume(); mThreadRun = true; mThread = new Thread(this); mThread.start(); }
スレッドを作成して、開始しているだけです。
mThreadRunにtrueを設定していますが、これはrun()内のループがwhile(true)ではなく、while(mThreadRun)になっているためです。
つまりmThreadRun=falseにすればrun()が終了するようにしています。
ひとつ注意点として、onResumeはアプリを再開ではなく、開始した時にも呼ばれます。
そのためonCreateではスレッドの開始をしていません。
次にonPauseです。
@Override protected void onPause() { super.onPause(); mThreadRun = false; while(true) { try { mThread.join(); break; } catch (InterruptedException iException) { } } mThread = null; }
mThreadRunをfalseにして、mThread.join()でスレッドの終了を待っています。
(joinの例外は発生することはないはずなので、無視します)
以上でonPause、onResumeへの対応は終わりです。
今回のサンプルコードをベースにすることで、SurfaceViewを使ったゲーム作成を、とりあえず開始することができます。
最後に一応、レイアウトファイル(activity_main.xml)も載せておきます。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".CActivityMain" > <SurfaceView android:id="@+id/surfaceView1" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </RelativeLayout>
レイアウト全体にSurfaceViewを配置しているだけです。
ピンバック: このサイトの改修について | takts – tak training site