ListView と CheckBox

  • 投稿日:
  • by
  • カテゴリ:

image

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)とか、そんな感じで呼んでやればパラメータがちゃんと生きるのかも……試してませんけれど。