LoginSignup
27
22

More than 5 years have passed since last update.

TimePickerの実装に闇があった(解決済み)

Last updated at Posted at 2015-05-21

Lollipopの前身であるAndroid L Previewが登場してから、もうすぐ1年が経とうとしています。皆さんもLollipop触ってますか?

今回は、LollipopのAndroid SDKコードでたまたま見かけたアレな状況を紹介したいと思います。

TimePickerの新UI

TimePickerはAPI Level 1のときから存在する、最も古いUIコンポーネントの1つです。

Lollipopでは、従来のロール方式での時刻選択に加えて、新たに時計の文字盤を模したUIが追加されましたね。これらはモード選択によって、どちらも利用できるようになっています。

近年のNexusユーザーにとっては、そろそろ慣れ親しんだコントロールになってきていることでしょう。

device-2015-05-21-223608 のコピー.png

spinnerモード
<TimePicker
    ...
    android:timePickerMode="spinner" />
clockモード
<TimePicker
    ...
    android:timePickerMode="clock" />

ハックしようと覗いてみたら

TimePickerを使っているとよくあるのが、「時計をN分刻みで選択できるようにしたい」という要件です。

これについては、StackOverflowを始めとして、いくつかの記事が公開されています。私はこちらの記事を参考にして、これまでTimePickerをハックしてきました。

簡単に言うと、「TimePickerはmMinuteSpinnerというNumberPicker型フィールドを持っているから、リフレクションでぶっこ抜いて制御しようぜ!」というやつです。

内部実装が従来とは違う

前述のとおり、TimePickerは必ずしもNumberPickerを並べただけのコントロールではなくなってしまったので、実装コードも大幅に変わっています。具体的には、SpinnerモードとClockモードのDelegateを作り、前述のandroid:timePickerModeの値に応じて内部実装を切り替えているのです。

ということで、前述のハックはLollipopでは動きません。一工夫する必要がありそうです。(この辺の話はまた別の機会に)

名は体を表・・・さない!?

新しいTimePickerのコードを掘り返していったところ、奇妙なものを見つけました。

スクリーンショット 2015-05-21 15.39.07.png
* https://github.com/android/platform_frameworks_base/blob/android-5.0.0_r1/core/java/android/widget/TimePickerClockDelegate.java
* https://github.com/android/platform_frameworks_base/blob/android-5.0.0_r1/core/java/android/widget/TimePickerSpinnerDelegate.java

あ・・・ありのまま、今、起こった事を話すぜ!

「Clock用DelegateがNumberPickerを持ち、Spinner用Delegateが文字盤ビューを持っていた」

な・・・何を言っているのか、わからねーと思うが 

おれも、何を見たのか、わからなかった・・・

頭がどうにかなりそうだった・・・デザインパターンだとか命名規則だとか

そんなチャチなもんじゃあ、断じてねえ

もっと恐ろしいものの片鱗を、味わったぜ・・・

API Level 21 の闇

というわけで、名前と実装が逆転しているのを見つけてしまったわけですが、実際問題、レイアウトXMLに対して timePickerMode を指定する分には、ちゃんと動作しています。クラス名が逆なのだから、動作も逆になってしまいそうなものですが・・・

と不思議に思いながら、TimePickerがDelegateを切り替えている部分に何となく目を向けてみました。

スクリーンショット 2015-05-21 18.11.54.png
https://github.com/android/platform_frameworks_base/blob/android-5.0.0_r1/core/java/android/widget/TimePicker.java#L87-L97

・・・

ん!?

スクリーンショット 2015-05-21 18.11.54 のコピー.png

こらこらこらこらー!!?

命名ミスったまま進んでしまったのか、何かのタイミングでRename系リファクタリングのミスがあったのかは知りませんが、宣言のたすき掛けによって、見事に前述の矛盾を誤魔化しています。

API Level 22 で直ってるよ

流石にこのままで放置されるはずもなく、現行最新版のAPI Level 22では、この問題は修正されています。

TimePicker.java
switch (mode) {
    case MODE_CLOCK:
        mDelegate = new TimePickerClockDelegate(
                this, context, attrs, defStyleAttr, defStyleRes);
        break;
    case MODE_SPINNER:
    default:
        mDelegate = new TimePickerSpinnerDelegate(
                this, context, attrs, defStyleAttr, defStyleRes);
        break;
}

ファイル名を取り替えるだけといえばだけなんですが、Git的には実装が丸々消されて書き換えられた感じになるので、修正コミットが大惨事になってました。

"Swap names for clock delegates so they are correct"
3 changed files with 1,444 additions and 1,444 deletions.
https://github.com/android/platform_frameworks_base/commit/daf33ed85353ab7d7a7668dd0e3f9a66f0d5583f

大変だなあ(他人事)

まとめ

面白いもの見つけちゃったなあと思いました(小並感)

27
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
22