NumberPickerを再作成する

AndroidのDatePickerやTimePickerは日付や時間等、数値の入力をスムースに行うための重要なビュー・コンポーネントなのだが

    • 入力範囲を設定する機能
    • 入力した値をフォーマットする機能
    • 入力する値を制限するフィルタ機能
    • 10, 20, 30..など決められた単位で値を増減する機能

これらの機能が無いので汎用的に使うことができずストレスが溜まる。

ソースコードを覗いてみると、内部で数値の共通に使用しているNumberPickerクラスは上記の足りないと思われる機能を全て持ち合わせており、これを拡張して自分なりのピッカークラスを作ればよさそうだ。
しかしNumberPickerクラスはご存じの通り@hideであり、android.jarに含まれておらず外部から使用することができない。
最初はリフレクションなどでベタにこなそうと思ったりもしたのだが、いい加減頭に来たのでNumberPickerクラスをSDKから抜いて自分のネームスペースで使うことにした。※1

といってもやることは簡単で、NumberPickerクラスに限れば、

1. githubから対象のバージョンのSDKの全てのソースコードとリソースをクローンする(私はAPILevel8 Froyoのを使った)
2. 以下のXMLを抽出して自分のライブラリィ又はプロジェクトの同一ディレクトリにコピーする

res/drawable/timepicker_up_btn.xml
res/drawable/timepicker_input.xml
res/drawable/timepicker_down_btn.xml
res/drawable-hdpi/timepicker_〜で始まる全ての9-patch pngイメージ
res/layout/number_picker.xml

3. 以下のソースコードを自分のライブラリィ又はプロジェクトのパッケージにコピーする

android/widget/NumberPicker.java
android/widget/NumberPickerButton.java

4. 自分の好きなようにカスタマイズしてビルドする。

これだけだ。
上記のように豊富な機能があるため、殆どの数値入力のインタフェースを作ることができるだろう。

  • NumberPickerを使うコーディング例
final NumberPicker monthPicker = (NumberPicker) yearMonthView.findViewById(R.id.pickerMonth);
monthPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
DateFormatSymbols dfs = new DateFormatSymbols();
String[] months = dfs.getShortMonths();

if (months[0].startsWith("1")) {
    for (int i = 0; i < months.length; i++) {
        months[i] = String.valueOf(i + 1);
    }
}
monthPicker.setRange(1, 12, months);
monthPicker.setSpeed(200);
monthPicker.setCurrent(currentMonth);

拙作のCalendarViewでは対象の年月を決定するためにダイアログを使用することができるが、そのユーザインタフェースとしてダイアログ上にNumberPickerを二つならべて使用している。

まんまSDKのNumberPickerなので、SDKのUIを使う限り統一した操作性を実現できる。※2

※1:なんでこんなに重要なクラスが非公開なんだろうか、全く腑に落ちない。何かに依存している? GingerBreadではUI全体が変わってしまうのか?

※2:SDK中のイメージを使用しているので他のリソースを差し替えている実機(Xperia、Desire等)では同じイメージにはならない。(フォーカス時のボーダー色や背景色、文字色が違う)