2012年1月14日土曜日

Android - Javaでの文字列から小数への変換

色々な国で使われるアプリだと小数点がコンマ(,)で表現されるところもあることを意識しましょう。主にヨーロッパとかロシア、南米ですね(参考)。

EditTextビュー等に入力された文字列を数値に変換する時に


String str = someEditTextView.getText().toString();
float v = Float.valueOf(str);

として値を取り出すとコンマが小数点だと認識されずに例外(NumberFormatException)が発生してしまいます。

小数点がピリオド(.)でもコンマ(,)でも正しく文字列を値に変換するには

String str = someEditTextView.getText().toString();
NumberFormat nf = NumberFormat.getInstance(); // デフォルトのロケールを使う
Number n = nf.parse(str);
float v = n.floatValue();

等としましょう。これならロケールに合わせてピリオドやコンマを正しく小数点と認識してくれます。

また値を入力するためのEditTextビューに

<EditText
    ...
    android:inputType="numberDecimal"
/>

と数字関係の文字のみ入力できるようにしている場合には

    android:digits="0123456789.,"

等と入力できる文字を指定してコンマも入力できるようにしましょう。

2011年12月30日金曜日

Android Service を自動的に再起動する方法

Androidで常駐するアプリを作るときはサービスを用いて基本的にはずっと起動させておく。ところが以下の場合にはサービスが停止されてしまう。その場合に再起動させる方法を以下に示す。


1) Android OSがメモリ等リソースが少なくなると強制的に停止する場合がある。
Service.onStartCommandの戻り値を START_STICKY 又は START_REDELIVER_INTENT にすることで、OSが勝手に再起動してくれる。


2) 電源が落とされた場合。
Intent.ACTION_BOOT_COMPLETED ブロードキャストを受けるレシーバを作成しそこからサービスを起動する。

Manifestには

<receiver android:name=".BootUpReceiver"
    android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
     </intent-filter>
</receiver>

等と記述。注意点としてRECEIVE_BOOT_COMPLETEDのパーミッションを取る必要がある。

このブロードキャストを受けるクラスとして

public class BootUpReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
            context.startService(new Intent(context, SomeService.class));
        }
    }
}

等としてサービスを起動しましょう。


***** 2012-10-21追記 *****
Android 4.04のスマフォ(XPERIA GXです)でテストしたところ、Intent.ACTION_BOOT_COMPLETED ブロードキャストが受け取れなくなっていた。
Manifestに


  <uses-permission
    android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>


を追加することで解決した。


3) アプリケーションをアップデートした場合。
Intent.ACTION_PACKAGE_REPLACED ブロードキャストを受けるレシーバを作成しそこからサービスを起動する。

Manifestには

<receiver android:name=".PackageReplacedReceiver">
    <intent-filter>
        <action android:name="android.intent.action.PACKAGE_REPLACED" />
        <data android:scheme="package" android:path="your.package.path" />
    </intent-filter>
</receiver>

等と記述。自分のものだけ受け他のアプリのものは除くようにandroid:pathを設定すること。

このブロードキャストを受けるクラスとして

public class PackageReplacedReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {
             context.startService(new Intent(context, SomeService.class));
        }
    }
}

等としてサービスを起動しましょう。


***** 2012-1-17追記 *****
どうもandroid:pathを設定しても全てのアプリのアップデートを受けてしまう様子。PackageReplacedReceiverクラスの方で



public class PackageReplacedReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
         if (intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {
             if (intent.getDataString().equals("package:your.package.path")) {
                context.startService(new Intent(context, SomeService.class));
             }
        }
    }
}

とIntentのDataをチェックしてからサービスを起動しましょう。


2011年12月25日日曜日

JavaのTimerとTimerTaskの罠

Androidアプリで定期的に行う処理を起動するのに

java.util.Timer



java.util.TimerTask

を使用してハマった件のメモ。

スマホの時刻を手動で一時的に先の時刻に変更すると(例えば今8時だったら9時とか)、時刻を元に戻したときTimerがまったく動かなくなる!

どうもTimer、TimerTaskはシステムの時刻を元に次に処理を起動する時刻を決めて、その時刻になったら処理を呼び出すという動作のようだ。そのため一時的に未来の時刻になるとその時刻を元に次に起動する時刻を設定してしまう。時刻を元に戻すとその未来の時刻まではうんともすんとも言わなくなってしまうようだ。

まあActivityのような画面に表示されているときだけ動作するものにはそれでも良いのだが、Serviceのようなバックグラウンドでずっと動くものには要注意である。
そういう場合は

Executors.newSingleThreadScheduledExecutor()
等で

ScheduledExecutorService

を作成しましょう。これなら上記の問題は無いです。

2011年12月1日木曜日

2011年7月10日日曜日

Tab ContentのActivityからServiceにbindする場合の注意

Tabで切り替えるActivityからServiceに以下の様にbindしようとすると、bindできない。

bindService(intent, connection, BIND_AUTO_CREATE);

解決法は

getApplicationContext().bindService(intent, connection, BIND_AUTO_CREATE);

とすること。
その場合はunbindも同様に

getApplicationContext().unbindService(connection);

としませう。

2011年6月5日日曜日

ssh(Ubuntu 11.04上)でX11転送する際の日本語入力ができない(かな漢字変換が動かない)件の解決法

sshでX11の転送を行ったとき(ssh -Y hogehoge)、日本語入力ができないという不具合に遭遇。その解決法のメモ(Ubuntu11.04どうしの通信)。
ローカル側の /etc/ssh/ssh_configに

SendEnv XMODIFIERS

リモート側の /etc/ssh/sshd_configに

AcceptEnv XMODIFIERS

を加えること。
リモート側は

sudo service ssh restart

でsshdを再起動すること。
こうすることでローカル側の環境変数 XMODIFIERS (Ubuntu 11.04(日本語版)ではデフォルトでは @im=ibus が設定されている)がリモート側に送られる。

2011年5月19日木曜日

Android Tab Layout の Icon についてのメモ

Tab Layoutのアイコンは低、中、高解像度のデバイス用にそれぞれアイコンを用意する。
アイコンのサイズ等の仕様はAndroid Developers の Icon Design GuidelinesTab Icons にかかれている。これによると


低解像度(ldpi) 中解像度(mdpi) 高解像度(hdpi)
アイコンサイズ 24 x 24 px 32 x 32 px 48 x 48 px
描画領域のサイズ 22 x 22 px 28 x 28 px 42 x 42 px
アイコンの置き場所
Android 2.0以上
res/drawable-ldpi-v5 res/drawable-mdpi-v5 res/drawable-hdpi-v5
アイコンの置き場所
Android 2.0未満
res/drawable-ldpi res/drawable-mdpi res/drawable-hdpi

アイコンの絵はサイズギリギリではなく、周囲に save margine として余白を置くよう推奨している。また絵が四角型の場合はバランスを取るためにもう少し小さくするように推奨している。

Androidでは2.0からタブアイコンのスタイルが大きく変わったので(どう変わったのかはよく知らない)、それぞれのアイコンの置き場所を表の様に分けている。注意点として Manifestファイルの <uses-sdk> の属性 android:targetSdkVersion を 5 以上(Android 2.0以上)に設定すること。たぶんこれでアイコンが上記の2つの場所に分けておかれていることを(Android 2.0以上の)システムが認識できる。

実際に(Illustrator、inkscape等で)アイコンを描く時には、各解像度のサイズの整数倍のサイズで書くように推奨している。 24, 32, 48 さらにメニュー用アイコンで使う72 px を考慮すると、これらの最小公倍数は lcm(24, 32, 48, 72) = 144 となる。よって144 x 144 px の整数倍のサイズの画像を作成し(例えば 864 x 864 px)、これを各サイズに縮小するよう勧めている。