2012年11月29日木曜日

【Android】各Activityの親について


AndroidアプリでActivityを複数持つ場合.
アプリとしての一貫性を保つためにすべてのActivityの親となるクラスを作っていた方がいい.
というか,作っていて当然というレベルであろうか.
同じ処理をあちこちに書くと修正の時に大変だし.

というわけでこういうクラスを作成した.



ここではAndroidの基本となる動作を簡略化してるだけだが,
アプリによってはこういったクラスがとてつもなく拡充されることだろう.
たとえばインターネット接続するアプリならバックグラウンドに行った瞬間にコネクションを切るだとか
フォアグラウンドに戻ってきた時にWiFi接続確認するだとか.
スキンを変更できるアプリなら画面作成時にSharedPreferencesから値を引っ張ってくるとか.
アプリのトップ画面への遷移もメソッド一発で実現できると楽なものである.

関係ないが管理している別のアプリでのSuperActivityクラスは1000行を超えてしまっていた…

2012年11月27日火曜日

【Android】ファイル名に使えない記号


Androidでファイル名に使えない記号は
> < : * ? " / \ |
です.
こんなかんじで全角に変換するなりなんなりで処理してあげましょう.


2012年11月21日水曜日

【Android】リブートした:Removing dead content provider: settings

オンキョーの古いAndroidタブレットTA117Cがリブートしくさった.
このタブレット自体は買い物でVeiwSonicのgTabletとかいうものの国内流通版とかいう話だが
そんなことはどうでもいい.


01-07 15:05:29.683: I/ActivityThread(1003): Removing dead content provider: settings

こんなログを吐いて死んだ.
Androidが再起動した.
ログを辿って行くとあらゆるサービスをServiceManagerが停止させて
起動中のアクティビティをActivityThreadが停止させて
それからリブートしているようだ.

よくは原因が分からないが
Removing dead content providerという文句に身に覚えが.
MediaStore…ContentProviderに登録している動画をギャラリーではなくファイラー(ESファイルエクスプローラー)から削除してしまったのだ.

つまりは多分ContentProviderというDBにエントリはあるものの,
動画本体は存在していない状態を作り出してしまった気がする.

しかしログを見ても再起動前にMediaScannerが動いていたようには見えないし
実際のところ何故リブートしたかは全く不明.

【Android】WebViewが倒せない


AndroidのアプリにWebViewを入れた.
DDMSでスレッド一覧を見た.

WebViewCoreThread
WebViewWorkerThread
CookieSyncManager
http*(←複数)

というスレッドたちが作られた.

その後,アプリを終了させるまで何をやってもこのスレッドたちを消すことができない.

WebViewをもつActivityを切り替えた
WebViewをもつActivityをfinish()した
WebViewインスタンスにnullを入れた
WebViewインスタンスをレイアウトからremoveView()した
WebViewインスタンスをdestroy()した
リフレクションからWebViewのonPauseを呼び出した

とりあえず調べてでてくるだけ試した.
でもこのスレッドたちは消えてくれない.
邪魔だ.
解決してない.しなさそうだ.

ステータスがwaitだからまだいいものの.

2012年11月15日木曜日

【Android】レイアウトを載せたダイアログの幅がwrapしてくれない

Androidのレイアウト.
どれだけ学んでも完璧にはいかないものである.

今日はレイアウトをセットしたAlertDialogの幅が
レイアウトにwrapしてくれないという問題で結構ひっかかっていた.

作ろうとしていたのはインテント送信先アプリ一覧ダイアログ.
iOS6のOpen Inみたいなレイアウトで3x3のグリッドをダイアログに表示させたい.

まず載せるレイアウトがこれ.
dialog_app_list.xml

そしてダイアログを表示させる元のjavaクラスがこれ.
MainActivity.java

本来ならGridViewに対していろいろ処理を行うんだろうけど
そんなことしなくても現象が発生するし
そういうことを書くとコード可読性が下がるから必要なモノだけ.

すると,こういう表示になる.
wrap_contentしているはずのLinearLayoutがAlertDialog全体に引き伸ばされている.
LinearLayoutとGridViewのbackgroundはこのために設定している.
つまり,この緑色部分に3x3のアイコンが並んでいると想像して欲しい.

我々が望んでいるのは画面幅に引き伸ばされたダイアログではなく
GridViewの幅ぴったりなダイアログである.
横幅いっぱいになるならレイアウト諦めて横一列にアイコンを並べられるだけ並べるとか
アイコンとアイコンの間隔がいっぱい開いたダイアログとか
そういったことはしないのである.
ここは見栄えのいい3x3にこだわろう.

ちなみに,LinearLayoutを潰して
dialog_app_list.xmlをGridViewだけのレイアウトにした場合
つまりこう変更すると

layout_widthの値を無視して
GridViewの幅が画面全体に引き伸ばされた.
つまり横幅いっぱいのAlertDialog全体が緑色に成った.
いちいち引き伸ばして…いらんおせっかいである.


どこを調べてもろくな情報がなかった.
悩みまくった結果,問題の本質はAlertDialogにあったらしい.
AlertDialogにsetViewするとレイアウト幅は何を指定しようと
画面幅まで引き伸ばされるようだ.
しかし,setViewしないAlertDialogはそういったことはない.
こういったときは一個上の親にお世話になりにいくべきか.

というわけでAlertDialogを使わずにDialogを使えば解決した.
ダイアログにセットするレイアウトはLinearLayoutを含んだ元のレイアウトに戻している.
ダイアログを表示するMainActivity.javaはこうなった.

するとちゃんとレイアウト幅に合わせたダイアログが表示された
…が!タイトル領域がデフォルトで表示されていて邪魔である.
サブクラスのAlertDialogが表示しないことが可能なのでDialogでも可能なはずである.

このタイトル領域はDialog.setContentViewする前に
Dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
とすれば消えてくれる.
つまり一行加えてMainActivity.javaはこうなって

表示が意図通りのものになって安堵した.

2012年11月14日水曜日

【Android】ListView/GridViewでGraphical Layoutにレイアウトを反映させる方法

AndroidのUIをeclipse上で組んでいくときに
Graphical Layoutといってレイアウトをeclipse上でプレビューする事が出来る機能がある.

まぁこれはAndroidを触ったことがあれば常識なのだが.
一応前口上としてこういうの書かないといけない感に襲われるから書いた.

このGraphical Layout,当該XMLに書いてあることしか表示できないので
javaコード上でレイアウトをinfalteするListViewやGridViewはそのままでは
全く違うレイアウトが表示されてしまう.

例として画像,テキスト,ボタンの3つを1つの列に表示させるListViewを考える.
その1列分がこのview_list_item.xml

それをバインドするactivity_main.xml

しかし,activity_main.xmlのGraphical Layoutは

こんな感じに表示される.
view_list_item.xmlをバインドするのはjavaコード上なので当然である.
これをちゃんと表示させるためにはactivity_main.xmlのListViewを少し修正すればよい.
activity_main.xml

変更点は
xmlns:tools="http://schemas.android.com/tools"

tools:listitem="@layout/view_list_item"
の2文を追加したこと.
この指定によってGraphical Layoutが

このように表示される.
よかった.



GridViewも同様である.
画像の上にチェックボックスをもたせたViewをGridViewに列挙させる.
1アイテム分のビューがこのview_grid_item.xml

で,それをバインドするactivity_main.xml

するとこう表示される

ListViewのときと同様に
xmlns:tools="http://schemas.android.com/tools"

tools:listitem="@layout/view_list_item"
の2文を追加してやればいい.
ただし,ListViewと違い,1点だけ気をつけなくてはいけないことがある.
それがGridViewのオプション
android:numColumns
である.
横に何個アイテムを並べるかというオプションで,
画面幅が様々なAndroidでは並べられるだけ自動で計算して並べてくれる
android:numColumns="auto_fit"
を使うことが多いと思うが,
このままではGraphical Layoutが認識してくれない.
Graphical Layoutが認識してくれるようにするためには
android:numColumns="-1"
としなければならない.

つまり,activity_main.xmlはこうなる.

ちゃんと表示された.

ちなみに
http://developer.android.com/intl/ja/reference/android/widget/GridView.html#attr_android:numColumns
によれば,auto_fitと-1はjava上では同値ということらしいが,
Graphical Layoutは-1でないと通じないようだ.
For input string: "auto_fit"
とかいって怒られる.
ハナシの分からんやつである.

一方,この値が-1のままだと今度はコンパイラに
Integer value out of range (at 'numColumns' with value '-1').
などと怒られてしまう.
そのため,レイアウト確認時は-1,それ以外はauto_fitに戻す必要がある.
うーん,馬鹿だ.

【Android】Buttonの背景に画像を指定するとレイアウトが崩れる


Buttonの背景に画像を使うとレイアウトが崩れる…
wrap_contentしてくれない…
そんなことに一瞬だけハマってしまった.

activity_main.xml

文字数が2文字,5文字,20文字のボタンを作っている.
それぞれ上から
・通常ボタン
・backgroundに色(#999999)を設定したボタン
・backgroundに9patch画像を設定したボタン
layout_width="wrap_content"を指定しているのに
文字数が少ない【OK】のボタンの時に
幅が広くなってしまっている.

こうやって並べるとなんとなく理由は分かると思うが
OKとキャンセルだけで組んでたときは
なんでOKとキャンセルとが等じ幅になるんだ!
とパニックになっていた.

理由は簡単.
背景に使っていた画像が
こんなやつだったため.

9patchというものは背景に設定したアイテムが広ければうまい具合に拡大してくれるが,
狭い場合には縮小はしてくれないようだ.
そのため,OKという文字列はこの9patch画像に対して
文字数が少なすぎて,wrap_contentできなかったのだ.

ボタン画像やインプット画像を作る場合は
幅の狭いもの画像を作るべきである.
9patch画像をこれに差し替えると
ちゃんとwrap_contentされた.


今回は関係ないけど背景が色指定の場合は
極限を目指したかのようにとてつもなく狭いため,
paddingを設定しないと見るに耐えないUIになる.

2012年11月12日月曜日

【Android】ListViewタップ時に勝手に表示される背景色を殺す


アプリケーションを個人で一人で作ってる時は
俺が仕様だとそれで済むのかもしれません.
しかし,チームで作ってるとなるとそういうわけにもいかず,
デザインチームがこうしてくれと言われたら本当に無理な場合を除いてそう実装しなければいけません.

今日はそんな感じでListViewを使ったメニューを作っていくなかで,
背景色に困った場合をスキットで紹介しましょう.

開発者を 開
デザイナを デ
と表記します.

デ「今度作ってもらうアプリのメニュー画面は拡張性や項目の表示/非表示にソフトで対応できるようにリストで作ってもらいたいです」
開「ふんふん,ListViewを使ったメニューActivityね.把握」

~1時間後~
開「こんなかんじで」
デ「いいじゃないっすか」
デ「メニューは文字だけじゃなくてボタン画像も表示します」
デ「これが通常状態で」
デ「これが押された状態です」


~30分後~
開「こうなりますね」
デ「いいですね,でも青いの邪魔ですね.なんですこれ」
開「AndroidでListViewの項目押されたら出てくるやつですね」
開「機種によって青だったり黄だったり橙だったりいろいろです」
デ「消せます?」
開「できますよ」

~数分後~
開「リストの背景白く塗りつぶすとこんなかんじです」
デ「塗りつぶし…だと…」
デ「メニューの背景…できた…」
開「背景…単色じゃないの?なんでマンデルブロ…」

~数分後~
開「そりゃこうなりますよね」
デ「ですよね」
開「マンデルブロ?」
デ「マンデルブロ」

~数分後~
開「これじゃダメ?」
デ「限りなく惜しいんだけど青いの消せません?」

さて,開発者くんはデザイナくんの要望に答えようと頑張りましたが
どうしてもListViewの選択色が邪魔でしかたありません.
背景が単色じゃないので塗りつぶしも出来なくなりました.
最初はListViewをカスタムして何とかできないかと思ったみたいですが
よのなかそんなにあまくはなかったのです.

しかし,解決法はありました.
1.ListViewのアダプタを拡張
2.各項目をバインド(getView)するときに各項目にクリックリスナをセットする
3.クリックリスナでリストの指定番目をクリックする動作を行う
これだけ.
3についてはabsListViewクラスにperformItemClickという関数があります.

まずはActivityのレイアウト.
activity_main.xml

次に通常時/タッチ時に切り替わる文字色とボタン画像
color_tapped_text.xml

list_menu.xml

そしてListViewのそれぞれの項目となるレイアウト
view_list_item.xml

最後にこのActivity自身である
MainActivity.java

これでUI上でListViewをタップした時に
view_list_itemのFrameLayoutがフックというかインターセプトして
ListView自体にタップ情報を通知しないようになりました.
そのため,選択色が表示されなくなりました.
また,MainActivityAdapterのgetViewで設定しているクリックリスナ中で
performItemClickメソッドを使い,親ListViewの指定番目をクリックしています.
遠回りをわざわざしているように見えますが,UIでクリックしているわけではないので
ListViewの選択色は表示されません.

重要なことなのですが,タップをインターセプトしたのはFrameLayoutなので,
その下の画像と文字色をタップ時に変更させたい場合は
view_list_item.xmlでしている通り,ImageViewとTextViewに
android:duplicateParentState="true"
オプションを追加する必要があります.

これで
開「こうなりました」
と言えるようになりました.

2012年11月1日木曜日

【Android】MediaStoreにサムネイルを作成させる

MediaStoreというAndroidがネイティブで持っているDBを使うと画像一覧が作りやすくなる.

Android自身が暇な時にSDカードの中を覗きに行って勝手に新しい画像を探してきて
DBに追加,サムネイルを作成してくれる.

いちいち手動でサムネイルを作成しなくても本体が作成してくれているから
その分の時間が省ける.
つまりはリスト描画の高速化が見込める.

たぶんこんな流れになっている
MediaScanner
⇒SDカード内の画像を収集,MediaStoreとサムネイル作成キューに追加

MediaProvider
⇒サムネイル作成キューを処理する

これらがAndroidで知らない間に行われている.
手元のXperia(SO-01B)で確認したところ,
MediaScannerが4枚画像を収集するぐらいの時間で
MediaProviderが1枚サムネイルを作成する.

つまり,DBへの画像追加が終わってもサムネイルは全て作成されていない.

なおサムネイル作成キューは電源を落としても消えないようだ.

MediaStoreに登録されている画像のサムネイルを取得しようとするとどうなるのだろうか.
リストに描画しようとサムネイルを取得する.
MediaStore.Images.Thumbnails::getThumbnail ()
というメソッドで取得するのだが,MediaStoreにサムネイルがない場合は律儀にも作成してから返却してくれる.
つまりそこで時間がかかる.

今回はそんなファイルたちのサムネイルを作成していこうと思う.
なお,うまいやり方が見つかるまでの一時しのぎのつもりである.

このクラスをActivity内に追加し,呼び出すとプログレスダイアログを表示し,
MediaStoreを片っ端から見ていってサムネイルがない画像のサムネイルを作成させる.

これで片っ端からサムネイルを作成していくことができる.
画像リストを作るがカーソルがあたってからサムネイルが作成されてその時々に描画を待つくらいなら
寝てる間に全て作ってくれたほうが嬉しいという場合にどうぞ.

なにか他にうまいやり方あればいいなぁとは思うが.