[android] DataBinding + Spinner

Android の Databinding で Spinner を使ったので、その方法をまとめておきます。

不満

Spinnerに調べていて思ったのですが、なんか使い勝手がものすごく悪くないですか?

公式を見てもらったらわかるのですが、固定の配列を下記のようにXMLに定義して読み込む感じになってるのですが、
こんなシチュエーションよりも、APIから取得してきたList配列を使って、もっと動的に配列の中身を変えるほうが多いのでは?と思うのですよ。

例えばこれが国の一覧だとすると、200個以上 <item>Japan</item> を書かないといけないわけです。ほんとにめんどくさいですねぇ。まぁ国の一覧なんて早々変更しないのであれですが、誤字脱字チェックなんてやりたくないですよね。
じゃあAPIから取得しようとなったときに、えっとListからはどうやってAdapterを作るの?となるわけで、また調査。めんどくさいですねぇ。
調べた結果、xmlからでなく、ListからAdapterを作成する方法が下記です。

ArrayAdapterのコンストラクタで、ちゃんとリストからの生成方法が用意されていたわけです。

Databindingで使うためには

上記のはちょっとした愚痴です。忘れてください。

さて本題です。
まず、Databindingを使う上で何を考えないといけないかというと、BaseObservableを継承したviewModelから、なんの情報をxmlにBindしたいのか?です。
ここでは、頻繁にSpinner内の内容が変わっていくユースケースを想定して、

  • 先程のListをBindすることを考えます。

それともう一つ、考えたいのが、

  • ユーザーがSpinner選択したときのコールバックをviewModelの内部で受け取れるようにする

とMVVMが実現できてよさそうです。

解答

上記を実現するために、下記のようにしてみました。

activity_something.xml

SomethingViewModel.java

まず、一つ目のリストを動的に渡すやり方ですが、xml 内のandroid:entries にviewModelからListを渡しています。個人的にはちゃんと動くのか自信がなかったのですが、内部でうまくCharSequence[]へ変換して、Adapterへの設定もやってくれます。ただ、スピナのデザインなどを変えるとなると、Custom Setters を使ってやるしかないかなと思います。

続いてコールバックについて。
コールバック部分はAdapterViewBindingAdapterというクラスにandroid:onItemSelectedが用意されているので、それにたいして、ラムダ式で設定します。

スピナーのsetOnItemSelectedListenerには、AdapterView.OnItemSelectedListener()を渡す必要があるのですが、このリスナーは下記のように2つのメソッドを持っているので、そのままではxmlに渡すことができません。そこで、上記のBindingAdapterを使うことで、それぞれ別々にリスナーを設定することを可能にしています。

ちなみに、よく似た名前で、setOnItemClickListenerのがあるのですが、 これを使うとRuntimeExceptionになるようです。サポーしてないからってException投げるってどうなんでしょう。Lintでエラー警告だすとかできないものでしょうか。

まとめ

スピナーってあまり使う機会がないようで、たまーに使いますよね。そんなときにはぜひこの方法で楽に実装してもらえたらと思います!

スポンサーリンク
レクタングル(大)