AndroidのAdapterViewで、カスタムビューを使っていてもCheckedTextViewなどのチェックをしたい(Spinnerのdropdown等)
AndroidのAdapterViewのAdapterにandroid.R.layout.simple_spinner_dropdown_itemなどを入れると、デフォルトで選択されているものをcheckedにしてくれます。
これは便利なんですが、ちょっと中のレイアウトをいじってLinearLayoutなどをitemの最上位オブジェクトにしてしまうと、チェックをしてくれなくなってしまいます。例えば以下。
foo.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:paddingLeft="5dp" android:paddingRight="5dp"> <ImageView android:id="@+id/FooImageView" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <CheckedTextView android:id="@+id/BarCheckedTextView" android:layout_width="fill_parent" android:layout_height="?android:attr/listPreferredItemHeight" style="?android:attr/spinnerDropDownItemStyle" android:singleLine="true" android:ellipsize="marquee" /> </LinearLayout>
ソースを見てみたところ、Adapterが、対象のitemがCheckableを実装しているかで判別してチェックをしてくれているようです。(android.R.layout.simple_spinner_dropdown_itemは上記のCheckedTextViewと同じものでした)
自前のAdapterの中でチェックを付けたり消したりするのは、Adapter的に(AdapterはデータとAdapterViewの橋渡しをしてりゃいいんだよ的な)よくないと思われるので、素直にカスタムビューを作ってCheckableを実装させることにします。
package jp.funnything.foo.adapter; import jp.funnything.foo.R; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.widget.Checkable; import android.widget.CheckedTextView; import android.widget.LinearLayout; import android.widget.ListView; public class SomeDropdownItem extends LinearLayout implements Checkable { private CheckedTextView _checkedTextView; public SomeDropdownItem( Context context ) { super( context ); setLayoutParams( new ListView.LayoutParams( ListView.LayoutParams.FILL_PARENT , ListView.LayoutParams.WRAP_CONTENT ) ); View view = ( ( LayoutInflater ) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE ) ).inflate( R.layout.foo , this , false ); _checkedTextView = ( CheckedTextView ) view.findViewById( R.id.BarCheckedTextView ); addView( view ); } @Override public boolean isChecked() { return _checkedTextView.isChecked(); } @Override public void setChecked( boolean checked ) { _checkedTextView.setChecked( checked ); } @Override public void toggle() { setChecked( !isChecked() ); } }
こんな感じにして、Adapter側では
@Override public View getDropDownView( int position , View convertView , ViewGroup parent ) { View view = convertView; if ( view == null ) { // よくあるパターンはこう /* * view = ( ( LayoutInflater ) getContext().getSystemService( Context.LAYOUT_INFLATER_SERVICE ) ).inflate( * R.layout.currency_spinner_dropdown_item , parent , false ); */ view = new SomeDropdownItem( getContext() ); } String item = getItem( position ); ImageView fooImageView = ( ImageView ) view.findViewById( R.id.FooImageView ); CheckedTextView barCheckedTextView = ( CheckedTextView ) view.findViewById( R.id.BarCheckedTextView ); // 適当にコンポーネントをいじる return view; }
としてやると動きました。
これだけなんですが、結構嵌ったのでメモ。
嵌りどころとしては、カスタムビューでinflateするときに
inflate( resId , null )だとinflateされたリソースのlayout_width,layout_heightがwrap_contentになってしまうっぽいので、
inflate( resId , this , false )とするあたりとかに嵌りました。