2012年10月25日木曜日

【Android】ArrayListのforループでエラーが起きて死ぬ


ArrayListのforループでエラーが起きて死ぬ

ArrayListのコレクションをforループで回して
その中で必要か不要かを判定して
不要ならループ内で消してしまうと死んでしまう.

例えば

こんなことをすると

java.util.ConcurrentModificationException
   at java.util.AbstractList$SimpleListIterator.next(AbstractList.java:64)

というエラーが起きて死ぬ.

残念ながらスマートではないけど違う方法をする必要があるようだ.

こうしてやる必要があるみたい.
本当はもっと違うところも書きたかったんだけど
まずは簡単なほう.

2012年10月22日月曜日

【Android】プログレスダイアログのバーを2つにする

ファイルのコピーなどでダイアログ中に2つのプログレスバーを表示したいという需要があるかもしれない.
Androidが持っているProgressDialogはとてつもなく便利なモノではあるが,
ただ単にAlertDialogを継承してそこにプログレスバーと文字に依る進捗表示を付け足しただけである.

プログレスバーにはセカンダリ値が設定でき,一応は2本のバーを同一軸上に表示させることができる.
しかしこのセカンダリ値の最大値を個別で設定することはできず,プライマリの最大値を用いることになる.

例えば,ファイルをコピーするときに進捗ダイアログを表示させたい.
プログレスバーを2本用意し,
片方には 送信したファイル数/送信済みファイル数 の進捗
もう片方には 処理中ファイルの送信済みバイト数/ファイルのバイト数 の進捗
を表示させたい.
こういった場合,最大値がそれぞれ違うためAndroidのプログレスバーを適用できない.

いや,使うことはできなくはないのだが.
最大値を100(%)にして
片方には 送信したファイル数(%) の進捗
もう片方には 処理中ファイルの送信済みバイト数(%) の進捗
をプログレスバーに与えてやれば確かにプログレスバーの動作としては問題ない.

しかし,文字で表示される進捗は
○○% 進捗/最大値 というフォーマットで固定されているため
X% X/100
という重複した内容が表示されることになる.
更には,セカンダリバーの表示はプライマリバーの後ろに,同じ太さで表示されるため
プライマリバーがセカンダリバーの進捗を追い越すとセカンダリバーが見えなくなってしまう.

当然ながら上に重なって表示されるバーは下のバーより細くなくては両方見えない.

というわけで,AndroidのProgressDialogのソースを元に2本のプログレスバーを表示するダイアログを作ってみた.

まずはリソース.
/res/layout/view_dual_progress_dialog.xml



このソースではセカンダリバーが上に細く表示されることになる.


次に,セカンダリバーがプライマリバーと同じ色だと見えないし,
バー背景が見えていたらプライマリバーが欠損してるように見えるので
セカンダリバーの色を変更する.
/res/drawable/progress_secondary.xml



実機ではプログレスバーが何色に表示されるかは機種ごとに違うため,
プライマリバーも同様の方法で変更した方がいい.
実際にこの例の緑だとサムチョンギャラクシーSIIで色が被ってしまった.


そして本体のjava
DualProgressDialog.java



最低限の実装しかしていないので,AndroidのProgressDialog同等の実装をしたいのであれば
ここに追加していけばいい.


なお,このDualoProgressDialogの使い方は通常のProgressDialogとほぼ同じになっている.
違う点というと,
円形進捗バーの存在がないので,
setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
をする必要がなくなったという点と
セカンダリプログレスの最大値を設定する必要がある
という点.
この2点である.

2012年10月19日金曜日

【Android】twiccaに画像インテントを投げてもアップロードできない

twicca
Androidのtwitterクライアントで100万ダウンロード以上,45,000レビューあって評価4.5という人気アプリ(2012/10月現在)
そのtwiccaに自分のアプリから画像をインテントで投げてtwiccaからつぶやいてみたい.
そういう需要があるかもしれない.

しかし,twiccaへのインテントの投げ方はどうやらお作法があるらしい.
普通に作ってインテントを投げてもtwiccaは画像をアップロードさせてくれない.


まず1点.
twiccaへ投げるmimetypeはちゃんとファイルに即した形式で投げる必要がある.
"image/*"などというワイルドカードは避けるべきである.
twiccaではこんなインテントは受けた瞬間に落ちるようだ.


twiccaへ投げるお作法でもう1点.

たとえばこんなかんじでtwiccaへインテントを投げる

File _file;
String _mimetype;
///////////////////////// 別のところで_fileと_mimetypeを設定

private void sendIntent () {
Uri uri = Uri.fromFile(_file);

Intent intent = new Intent (Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.setType((_mimetype != "") ? _mimetype : "image/*");
startActivity(intent);
}

やってることは単純.
ファイルからURIを取得してそれをインテントとして投げている.

他のだいたいのアプリはこれできちんと受け取ってくれるのだ.
でもtwiccaはクリップボードに画像がありますよマークは出るのだが
アップロードボタンを押しても反応しない.


さて何故でしょう.
ちなみにAndroid標準ギャラリーから送った画像はtwiccaは正常にアップロード出来て
開発者の友達,ESイメージブラウザから送った画像は今回と同様にアップロード出来ない.


ここから推測するに,twiccaはcontentスキーマしか処理していない.
contentスキーマというとContentProvider用のスキーマ.
URLがcontent://から始まる.

一方,上のサンプルでtwiccaへ投げたURLはFileパス.
FileパスのURLはfile:///から始まるのである.

ちなみに同一画像についてギャラリーとESイメージブラウザからインテントを投げてもらって
受け取ったURLが以下のとおりである.

// ギャラリー
URI: content://media/external/images/media/2003
// ESイメージブラウザ
URI: file:///mnt/sdcard/DCIM/Camera/20121015_200741.jpg

同一画像だが,全く違うURLをくれた.


あくまで俺の勝手な推論だが,
twiccaはcontentスキーマしか処理してないと考えられる.

画像を受け取るアプリケーションの理想としては
他アプリからインテントを受け取ったときに
URLの先頭を確認してcontentから始まっていたらfile形式のURLに変換するか
もしくはfileから始まっていたらcontent形式に変換するかをして
両URLに対応するのが望ましいと思うが,実際にcontentスキーマしか対応していないのなら
送り側がfileスキーマからcontentスキーマに変換してやるしかない.

しかしそうすると逆にfileしか対応していないアプリに対してエラーを出させることとなる.
投げる側は1つのURLしか投げられない.


ちなみにcontentとfileの相互変換については他サイト様を参考にされたし.
http://yagni.jp/android/interconversion_between_file_and_content