AndroidのListViewを使っているとき、行をタップして選択したいことがあります。と、いうか、普通選択したいです。
そんなときには、ListViewにOnItemClickListener()を与えて、イベントを受け取れるようにすればいいわけです。しかし、ある条件下に置いて、この機能は全く機能しなくなってしまうのです。
通常、ListViewの行がタップされたことを検知するのは、次のようなコードになります。
ListView lv = (ListView)findViewById(R.id.listView);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id){
// callback
}
});
ほとんどの場合、これで、アイテムがタップされたことを検知して、このコールバックが呼び出され、処理を行なうことができます。
問題は、ListViewの行の中身を独自に定義している場合かつ、そこにCheckBoxや、CheckedTextViewなどのFocusを取り得るウィジェットが存在している場合に起こります。それまで動作していたコードが、これらのウィジェットを追加した途端に、呼び出されなくなるのです。
原因は、書いたとおり、Focusableなウィジェットにあるので、これをFocusableでなくしてしまえば、再び onClickItemが拾えるようになります。
問題は、その方法。リソースの定義で Focusable, FocusableInTouchModeをfalseにしても、実はうまくいきません。LayoutInflaterで、View をinflateした直後に、これらを直接、falseに書き換える必要があるようです。
Adapterを定義する時に、getView()あるいは、newView()をオーバーライドして、その中で、やるのが良さそうです。わたしは、CursorAdapterを利用したので、newView()の中でやりました。
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// TODO 自動生成されたメソッド・スタブ
View v = layoutInflater.inflate(R.layout.list_item, null);
CheckBox checkBox = (CheckBox)v.findViewById(R.id.checkBox);
checkBox.setFocusable(false);
checkBox.setFocusableInTouchMode(false);
return v;
}
この、たった二行を追加しただけでOK。逆に、これがないと、アイテムをタップしても何も起きません。長押しでコンテキストメニューが出るように指定したつもりでも、それも効きません。(物理キーやトラックボールによるSelectは効くので、OnSelectedItemListenerは機能します。)
たったこれだけのことに随分と何日も悩んでしまいました。いえ、ググると android:focusable="false" でうまくいくよ、とか書いてあるところが結構あったのですが、ダメで、かといって、bindView()の方でデータをセットするついでに……と、setFocusable(false)してもダメで、途方に暮れていたんですよ。半ばやけ気味に、newView()の方でやったらビンゴでした。
ただ、android:focusable="false"の方については、もしかすると、inflate()の仕方が悪いのかもしれません。inflate(R.layout.list_item, this, true)とか、そんな感じで呼んでやればパラメータがちゃんと生きるのかも……試してませんけれど。
コメント