AndroidのSpinner#setSelection()にバグ?

前置き

AndroidのSpinner#setSelection(int position)にバグがあるような気がするので書いておきます。

[2011-04-25]setSelection()をsetPosition()と誤記してたので修正しました。

本文

環境

  • API Level8(Platform2.2, Froyo)
  • emulatorで実行

コード

  • Activity#onCreate()時にSpinnerを持ったAlertDialogオブジェクトを生成する
  • AlertDialogオブジェクトは、ずっと再利用する(ダイアログを開くたびにnewするわけではない)
  • onCreateDialog()では、そのAlertDialogオブジェクトをreturnするだけ
  • onPrepareDialog()はオーバーライドしない
  • ダイアログを閉じるたびにremoveDialog()する
  • ダイアログを閉じるとき、Spinnerの選択位置を覚えておく →Spinner#getSelection()
  • DialogInterface.OnShowListener#onShow()時に、ArrayAdapterを準備してSpinnerにセットし、前回閉じたときの選択位置へsetSelection(position)する
  • 初めてダイアログを開くときは、setSelection(0)する

現象

2回目以降にダイアログを開いたとき、setSelection(position)で指定したpositionの値に関係なく、Spinnerに0番目のテキストが表示されます。

発生手順

Spinnerに、"A"、"B"、"C"の3つが登録されているとします。

  1. ダイアログを開く → Spinnerには"A"が表示される
  2. Spinnerをタッチ → Spinnerがドロップダウンし、"A"が選択されている
  3. "B"をタッチ → Spinnerが閉じ"B"が表示される
  4. ダイアログを閉じる
  5. ダイアログを開く → Spinnerには"A"が表示される
  6. Spinnerをタッチ → Spinnerがドロップダウンし、"B"が選択されている

このように、ドロップダウンしてみると、ちゃんとsetSelection()が効いているのが分かります。

妙なことに、ダイアログを開くときにsetSelection(2)するようにコードを修正すると、上記の現象は発生しません。つまり、手順5のところで、ちゃんとSpinnerに"C"が表示されます。いろいろ試してみると、前回の選択位置へsetSelection(position)したときだけ発生するようでした。

回避策1

Spinnerにadapterをセットしたあとで、Adapter#notifyDataSetChanged()すると回避できます。viewの再描画が起きるおかげでしょう。

ただし、Spinnerに対してinvalidate()しても回避できませんでした。

回避策2

setSelection(int position)の代わりにsetSelection(int position, boolean animate)を使うと回避できました。こっちを使えばSpinnerの再描画が起きるようです。animate引数の意味が不明(リファレンスの説明も意味不明)ですが、trueでもfalseでも動作上の違いは見当たりませんでした。

回避策3

私のコードに問題がありそうな気もしています。ダイアログを閉じるたびにremoveDialog()しておきながらAlertDialogオブジェクト自体は再利用するというのは、定石から外れているかもしれません。また、Spinnerへのadapterセットを、onShow()ではなくonPrepareDialog()のタイミングで行うべきかも。

後日談: onPrepareDialog()でadapterをセットし、onShow()でsetSelection(position)すれば回避できました。しかし、onPrepareDialog()でadapterセットとsetSelection(position)するとダメでした。

グチ

AndroidのGUI部品の中でもSpinnerは好きになれません。未選択状態にできないし、見た目もカッコ悪いし。

Last modified:2011/04/25 15:16:08
Keyword(s):
References:[Androidアプリ開発]
This page is frozen.