|
~ To be, or not to be, or to let it be. ~ 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で判断しようとしており、 別の例だと、 忘れる前に、いくつかメモしましたが、 |
|