Context ActivityかApplicationか
ファイルダイアログを作るためにAlertDialogをベースにした外部クラスを作って呼び出したら詰まった。
new AlertDialog.Builder(Context context)
なぁなぁでやってるのが悪いのだけれども、このcontextになにを入れるべきか…
AlertDialog自体を取り扱っている解説は大体contextにthisを当てている。
ただこの場合、thisを入れれば例外が当然発生する。
このクラス(のアクティビティ)が呼び出しているわけじゃないから正しくない(たぶん)。
thisは便利なものだが、解説の場だと素人にとってなにを指しているかわからない。
完全な恥さらしの言い訳だけど…
で、Contextについて調べた。
内容は解説してくれているサイトがたくさんあるので割愛。
ただ、Activity ContextとApplication Contextが(おおまかに)あり、
Activity Contextはthisを、Application ContextはgetApplicationContext()が代表的に使われている。
ライフサイクル上メモリリークが発生するかもしないかもでgetApplicationContextが推されている…
ような書き方が多い。
とりあえず試してみた。
Intentなどはどちらでも機能する。
実行中の(Activity)クラスでAlertDialogを生成する場合
クラス名.thisは成功
getApplicationContext()はWindowManager BadTokenException
あと、getBaseContext()も同例外発生
今回のように実行中の(Activity)クラスから(ファイルダイアログ用)外部クラスを呼び出してAlertDialogを生成する場合
- context自体に
- this 不可(IlleagalStateException発生)
- getApplicationContext() 不可(WindowManager BadTokenException発生)
- 呼び出し元のActivityクラス(AAA.class)からContextを受け取る。
- AAA.thisをContextとして渡す 可能
- getApplicationContext()を同上 不可(WindowManager BadTokenException発生)
getApplicationContext()は向かないようで、例外が発生する。
結局呼び出し元のクラスからActivity Contextを渡して、AlertDialogの生成に当てると機能する。
解説を見てて違和感があったけど、結局のところ適材適所みたい。
そもそもを理解した上でメモリリークに注意した使い分けろをしろということなんだろう。
あたりまえですよね…
以下、今回のことと近いようなことをきちんと解説しているサイトとこの後詰まるであろうnull対処の引用
ものぐさDev AndroidのContextについて
http://monogusadev.blogspot.jp/2012/08/androidcontext.html
FLYING AndroidアプリでContextを持ち回したい話
http://d.hatena.ne.jp/tondol/20120616/1339786005
{Android]Activityの終了とアプリケーションの終了
Android で System.exit() を使ってはいけない理由と、終了方法のまとめ
http://www.ecoop.net/memo/archives/2011-01-24-1.html
Activityを終了するときfinish()を
アプリケーション全体の終了はmoveTaskToBack(boolean nonRoot)が推奨とのこと。
ただ、moveTaskToBackはバックグラウンドで待機になる。
この内容に沿って複数のActivityを並べたときの終了と起動画面(タイトル)への
切り替えを実装したくぐちゃぐちゃしたのが前回まで。
とりあえず希望通りの動きでそこそこまとめてできた。
条件は前回と同じく0〜3のActivityを作成(act0〜act3)
順番にIntentで遷移し、act3で分岐する。
分岐はタイトル(act0)へもしくはアプリケーションの終了。
ここを参考にフラグクラスの作成。
act0
public class act0 extends Activity implements OnClickListener{ static boolean exitFlag; @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.aaa); exitFlag = false; Button next = (Button)findViewById(R.id.button0); next.setOnclickListener(this); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public void onRestart(){ super.onRestart(); if(exitFlag == true){ this.finish(); } } @Override public void onClick(View view){ switch(view.getId()){ case R.id.button0: startActivity(new Intent(this, com.xxx.act1); break; } } }
act1
public class act1 extends Activity implements OnClickListener{ public static Flag actFlag; @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.bbb); flag = new Flag(); Button next = (Button)findViewById(R.id.button1); next.setOnClickListener(this); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public void onRestart(){ super.onRestart(); if(act1.actFlag.getFlag() == true){ this.finish(); } } @Override public void onClick(View view){ switch(view.getId()){ case R.id.button0: startActivity(new Intent(this, com.xxx.act2); break; } } }
act2にも同様のonRestart()を実装する。
act3
public void act3 extends Activity implements OnClickListener{ @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.ddd); act1.actFlag.setFlag(true); Button title = (Button)findViewById(R.id.button3_1); title.setOnClickListener(this); Button exit = (Button)findViewById(R.id.button3_2); exit.setOnClickListener(this); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public void onClick(View view){ switch(view.getId()){ case R.id.buttonTitle: this.finish(); break; case R.id.buttonExit: act0.exitFlag = true; this.finish(); break; } } }
最後のActivity(act3)がonCreate()した時点でactFlagを立てる。
これでact1〜act2はonRestart()した時点でfinish()するので、表示されない。
「タイトルへ」のボタン操作にはIntentでact0へ遷移するのではなく、
このActivityをfinish()するだけ。
上記内容でact1〜act2が表示されないのでact3自身をfinish()すればタイトルへ。
また、act0には終了用フラグ(exitFlag)を用意しonCreate()されると
falseが格納されるようにしておく。
act3の「終了」のボタン操作でexitFlagにtrueを格納し、act3自身をfinish()。
「タイトルへ」同様act0へ戻るのでonRestart()内でexitFlagを判別し、
trueだったらact0をfinish()すれば表示されずそのままアプリケーション終了。
素人なりのやり方かもしれないけどとりあえず二つのフラグを使って解決。
Intentに気をとられすぎた…
ところでonClick()ってボタンにsetOnClickListener()した直後の方がいいんだろうか?
{Android] 今日あったこと
複数のActivityを作成。
このActivity群を順々に遷移させ、最後のActivityにルート分岐させる。
ルート分岐はタイトル(最初のActivity)に戻る・アプリケーションの終了・その他。
今回はタイトルへ
仮にActivityを0〜3まで作成。(act0〜act3)
その順番通りに上記内容を再現。
ここを参考にフラグクラスを作成し、act0〜2までonRestart()でフラグ変数の値を取得し、
trueならばfinish()するようにした。
また、act3にはonCreate()内でフラグ変数にtrueを格納し、表示したタイミングでフラグを立てるようにした。
act3からボタン操作でintentを生成しact0へ遷移と共にact3自体をfinish()させる。
しかし実行すると、act0へ遷移後に戻るを押すとact2が表示される。
そのままact2→act1→act0と遷移。
intentでよく使われる「遷移」という言葉に素人なのでうっかり気を取られるけど、
これは起動時に表示したact0ではなく新しく生成したact0、言わばact0'と思われる。
こうなるとまた新たにフラグクラスが生成・格納されるわけで、
act3が表示されたタイミングでフラグ変数に格納したtrueが初期化される(?)。
となると、onRestart()でフラグを期待通りに判断できずこのような現象になったと思われる。
とりあえずact0の扱いは置いておくとしてact1〜2のみにonRestart()での処理を行うよう変更。
実行で同様にact0へ遷移。
ここで戻るを押してもact2には行かないが、さらにact0が出てくる。
act3→act0'→act0
今回は一枚多いact0を抜ければ終了されるが当然不恰好。
期待通りに動いた解決方法は、
アプリ終了用に作っておいた別のフラグ変数を組み合わせることで可能になった。
こちらはintentにputExtraで付与しておくintフラグ(仮:0or1で判断させたため)。
act0のonCreate()内にintentの受け取りと付与情報を取得してintフラグが1ならそのままfinish()
するようにしておくことでできた。
act3(フラグにtrue格納)→タイトルボタン(intent&intフラグに1格納)
→act0(というよりact0'が生成されるもintフラグ判断によりそのままfinish())
色々思い当たるところはあるが…もうよくわからない。
ていうか、もっとスマートにしたい…
inflate使用で呼び出したレイアウト内Viewに対するfindViewById
レイアウトのテンプレートを作っておきボタン操作により、
Activityのとあるレイアウト内で切り替え表示ができるようにしたいため、
表示用xmlにテンプレートxmlをincludeした上でInflateを使用しその機能を実装。
(最初にViewFlipperを利用し絶望しかけた…)
このままだとテンプレートxmlのまま各Viewが表示されるのでfindViewByIdで参照を得て、
それぞれにsetText()なりしようとしたら詰んだ。
流れとして
template1.xml
<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/C" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="none" /> </LinearLayout> </merge>
A.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <include android:id="@+id/B" layout="@layout/template1" /> </LinearLayout>
activity.java
省略 @Override public void onClick(View view){ switch(view.getId){ case R.id.xxxBtn: layout.removeAllViews(); getLayoutInflater().Inflate(R.layout.A, layout); LinearLayout linear = (LiearLayout)findViewById(R.id.B); TextView text = (TextView)linear.findViewById(R.id.C); text.setText("change"); break; } }
上記内容でActivity.javaのLinearLayout linear = (LiearLayout)findViewById(R.id.B);がnull
「include関連で調べた内容と違いがないのになんでできないのか…」と
しばらくの現実逃避の後、頭を捻らせた結果
switch(view.getId){ case R.id.xxxBtn: layout.removeAllViews(); View inf = getLayoutInflater().Inflate(R.layout.A, layout); TextView text = (TextView)inf.findViewById(R.id.C); text.setText("change"); }
これで解決。
Inflate部分に変数を持たせてLinearLayoutを中継せずそのまま対象の参照を取ればいい?
Inflateを利用したことで表示されるようになったから参照できると思ったらそうはいかないみたい。
Inflateを利用するしないでincludeしたものの変更方法に違いが出たという結果…であっているのかな?
スライドショーと切り替え
Androidのレイアウトに四苦八苦…
あるActivityからボタン操作でルート分岐したかったのだが、
それぞれActivityを作ってそれに飛んで…とすると
どうも操作性に欠ける気がするし、全体の工程が長く感じてしまう。
となるとそのActivity内で表示させてしまえばいいと思った次第。
Activity内でViewの動的な変更を行えるのが「ViewFlipper」
対象となるレイアウト群を作り、設置したViewFlipperにincludeする。
xxx.xml
省略 <ViewFlipper android:id="@+id/vFlipper" android:layout_width="wrap_content" android:layout_height="warp_content" > <include android:id="@id/flipA" layout="@layout/A" /> <include android:id="@id/flipB" layout="@layout/B" /> <include android:id="@id/flipC" layout="@layout/C" /> </ViewFlipper> 省略
activity.java
省略 ViewFlipper flipper; @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.xxx); Button btn1 = (Button)findViewById(R.id.button1); btn1.setOnClickListener(this); Button btn2 = (Button)findViewById(R.id.button2); btn2.setOnClickListener(this); flipper = (ViewFlipper)findViewById(R.id.vFlipper); } 省略 @Override public void onClick(View view){ switch(view.getId) case R.id.button1: flipper.showNext(); break; case R.id.button2: flipper.showPrevious(); break; }
こんな感じでincludeした順番で次へ行ったり戻ったり表示を変えることができる。
また、startFlipping()とstopFlipping()でスライドショーも可能。
手動だけでなく時間設定をした自動スライドショーも可能。
ボタン操作だけでなくonFlingメソッドを作ればタッチ操作での切り替えも可能。
と、便利だけどあくまで順番に表示する進む戻る切り替えらしく
ボタン操作による完全なルート分岐とは違う。
いわゆるTVのチャンネルとリモコンの関係を作りたい。
そこのでInflateがある。
こちらはxml側で纏めるのではなく、表示用xml郡を作ってJava側で表示の切り替えを行う。
なのでViewFlipperではなく、なんらかのレイアウトを配置。
activity.java
省略 LinearLayout layout; @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.xxx); Button btn1 = (Button)findViewById(R.id.button1); btn1.setOnClickListener(this); Button btn2 = (Button)findViewById(R.id.button2); btn2.setOnClickListener(this); layout = (linearLayout)findViewById(R.id.target); } 省略 @Override public void onClick(View view){ switch(view.getId) case R.id.button1: layout.removeAllViews(); getLayoutInflater().inflate(R.layout.A, layout); break; case R.id.button2: layout.removeAllViews(); getLayoutInflater().inflate(R.layout.B, layout); break; }
ボタンが押して対象のレイアウト内のViewをすべて消し、
Inflateで新たに表示することで切り替えが行われるようにするみたい。
こうしてレイアウトが対応されたボタン毎に切り替えて表示できる。
使い分けが必要。
Swingにおいてフォント(文字描写)が汚いので対策
swingで太字(BOLD)を利用すると10数年前の気分に浸れるので、どうにか回避したい。
アンチエイリアスというボカシの一種が効いてないようなのでドットで打った文字みたいに…
引用 Java技術最前線 「Java SE 6完全攻略」第17回 文字に対するアンチエイリアス http://itpro.nikkeibp.co.jp/article/COLUMN/20070205/260649/ Q's Swing Lab - java GUI customization - http://qoofast.blog76.fc2.com/blog-entry-19.html
詳しくは引用先などを参照
自分の環境ではpaintComponentでRenderingHintにて各コンポーネントに
アンチエイリアスをオンに指定しても変化がなかった。
恐らくなにか足りないんだろうけどここに今時間かけられないので…
メインメソッドに以下を追加
System.setProperty("awt.useSystemAAFontSettings","on");
System.setProperty("swing.aatext", "true");
で、とりあえず解決
引用 プログラム問答 http://ja.softuses.com/40958
エミュレータの重さ解消方法と仮想デバイスAndroid 2.3.3での問題
Android SDK Managerからintelのエミュレータイメージをインストールする云々(下記引用)
引用元
【番外編】Androidの爆速エミュレータ環境を構築する
http://dev.classmethod.jp/smartphone/build-fast-android-emulator/
噂の爆速エミュレーター(android)を試してみたがマジで早かった件。
http://ウェブ制作日記.com/%E5%99%82%E3%81%AE%E7%88%86%E9%80%9F%E3%82%A8%E3%83%9F%E3%83%A5%E3%83%AC%E3%83%BC%E3%82%BF%E3%83%BCandroid/
インストール後にAndroid 仮想デバイス・マネージャーにてAndroid 2.3.3対象で作成しようとすると
Unable to find a 'userdata.img' file for ABI x86 to copy into the AVD folder.
と表示されて作成ができない。
調べてみるとインストールされるディレクトリが間違っているっぽい。
...\sdk\system-images\android-10\x86に入れるべき内容が
...\sdk\system-images\android-10\x86\images\x86に入っているみたい。
消すのも怖いので
...\android-10\x86\images\x86の内容を...\android-10\x86にコピーで解決。
引用元
stackoverflow
http://stackoverflow.com/questions/14913852/cannot-create-2-3-3-intel-atom-avd-userdata-img-not-found