ゲームプログラミングなどで SurfaceView に描画すると早いと思って、使ったは良いがなぜか描画されず、ネットで記事を検索してもうまく行かなかくてハマった。
これはその問題を解決するまでのドラマである。
w
まず、考えておくこととして、ゲームプログラミングの本を買った方が良い(もしくはスクール、セミナー受講)。体系的に書いてあるし、動くコードが掲載されている。ゲームプログラミングとアンドロドアプリ特有の事情が絡んでいるので、初学者には難しい。
最初のハマりポイント。
Q. SurfaceView が 再描画イベントを感知しないので、描画できません!
A. まず、Android OS は UIスレッドの中で再描画しません。別スレッドをおこしてください。
そもそも、SurfaceView はゲームなどの高速描画を行うための View なので、わざわざ invalidate() をしたりして、 UI イベントを送って再描画するような使い方は通常はしないと思います。
なぜなら、UI イベントを送るというお行儀の良いやり方は、「システムの都合の良いときに描いてくれればあり難き幸せ」と言っているのと同じで、速度が犠牲になっているからです。
なので、ゲームでもそれほど速度が重要でないチェスや将棋のようなゲームにはそのような描画の仕方で良いといえます。その場合は、通常の View を使うはずです。
それでも、なんらかの事情で SurfaceView を使って UI イベントを送って描画させたいケースがあるとすれば、SurfaceView のサブクラスで setWillNotDraw(false); としなければなりません。
その後は、普通にスレッドを起こしてループを書き、そのなかで invalidate() を呼び出して、SurfaceView のサブクラスのOnDraw()を呼び出すようにしてください。
もちろん、この描画の方法は、SurfaceView を使っているからといって速くなりません。
Q. SurfaceView で描画専用のスレッドを起こして、unlockCanvasAndPost() を呼び出したのに、まだ実行できません!
A. これにはいくつかの原因があります。
- まず、SurfaceView のサブクラスに SurfaceHolder.Callback というインターフェースを継承し、surfaceChangedというメソッドから描画スレッドを開始すること。
- Thread を開始するときに呼び出すのは run() ではありません。start() にしないと非同期になりません!
- lockCanvas -> 描画 -> unlockCanvasAndPost を行って描画する。
ちなみに、SurfaceView を使って描画イベントすっ飛ばして自分で描画してみたところ、ゲームの動きが非常にスムーズになりました。