~ To be, or not to be, or to take a lunch anyway. ~ null-i.net |
AndroidJava/少し長いHelloWorld(SurfaceView) | |
少し長いHello World(2018-10-20) とりあえず、Android Studio を使って、 アプリで、画面がアニメーションするようなのを作る場合は、 大まかな流れとしては、
まず、MainActivityをつくる†まず、Android Studio で プロジェクトを新規作成します。
すると、たぶん最初にこんなのができますよね。これは後で差し替えます。 public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } } で、この時点で一旦アプリをビルドしてみた方が良いかもしれません。 アプリのテーマの変更(アプリ名表示の削除)†まずは、画面のテーマを変えます。 (変更前) <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> (変更後) <style name="AppTheme" parent="Theme.NoTitleBar"> これで画面がスッキリします。 SurfaceView で Hello world†MainViewという名前でSurfaceView のクラスを作ります。 長いけれど、やりたいことは最後の「drawView」で Hello world を画面出力することです。
class MainView extends SurfaceView implements SurfaceHolder.Callback { private static final long LOOP_INTERVAL = 1000; MainView(Context context){ super(context); getHolder().addCallback(this); } private class MyThread extends Thread { private final AtomicBoolean isEnd = new AtomicBoolean(false); void end(){ isEnd.set(true); } @Override public void run(){ SurfaceHolder holder = getHolder(); while(!isEnd.get()){ if(holder.isCreating()) continue; Canvas canvas = holder.lockCanvas(); if(canvas == null) continue; drawView(canvas); holder.unlockCanvasAndPost(canvas); synchronized (this) { try { sleep(LOOP_INTERVAL); } catch (InterruptedException e) { } } } } } private MyThread thread; void startThread(){ stopThread(); thread = new MyThread(); thread.start(); } void stopThread() { if (thread == null) return; if(thread.isAlive()) thread.end(); while(thread.isAlive()){ try { Thread.sleep(LOOP_INTERVAL/5); }catch (Exception e){ } } thread = null; } @Override public void surfaceCreated(SurfaceHolder holder){ } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height){ startThread(); } @Override public void surfaceDestroyed(SurfaceHolder holder){ stopThread(); } void drawView(Canvas canvas){ canvas.drawColor(Color.BLACK); Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); p.setColor(Color.WHITE); p.setTextSize(canvas.getWidth() / 20); long sec = System.currentTimeMillis() / 1000 % 60; canvas.drawText("Hello World: " + String.valueOf(sec), 100, 100, p); } } まぁ、試しのコードですし、 MainActivityの更新†上記のSurfaceViewを踏まえて Hello worldを実装します。
public class MainActivity extends Activity { private MainView mainView; RelativeLayout mainLayout; private static int PID; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if(PID != 0 && PID == android.os.Process.myPid() && mainView != null){ return; } PID = android.os.Process.myPid(); if(mainView == null ) { mainView = new MainView(this); } mainLayout = new RelativeLayout(this); mainLayout.addView(mainView); setContentView(mainLayout); } } とりあえずこれで、 ...されなければ、Log.d を大量に投入しましょう! もう少し処理が複雑になった場合に、困ることの例†自分が何度もやらかした(やらかしている)ことの例で まずはデバッグログ!!†Log.d や、自作のLog.dラッパークラスを入れましょう!! デバッガを使いこなせば良いのでしょうが、 ログから該当箇所を探すのが面倒なら、 ループ部分に入れると、大変な数になりそうですが、 Threadについて†上記では「private MyThread thread;」で1つだけ作っているスレッドですが、 スレッドを分けないと、データが増えたときにアプリがダウンします。 そして、処理を分けるためにスレッドを増やすと、排他制御が困難になります。 最初は単に synchronized で排他すれば良いと思っていたんですが、 これらの問題が厄介なのは、実機の動作確認で検出できる可能性が低い点、です。 ライフサイクルについて†前述のThreadがいつ開始、停止するのか 私の場合に問題発生がより分かり易くなったのは、アプリに音楽を入れた後です。 それが音楽ではなく画像イメージの decode/recycle 場合は、メモリリークでダウンします。 アプリのプロセスがダウンするタイミングについて†アプリをバックグラウンドに移した後に再開する場合に 上記のアプリの例だと、その違いをPIDで判断しようとしており、 別の例だと、 忘れる前に、いくつかメモしましたが、 |
|