ButterKnife、便利ですよね。Activity が沢山のなんちゃら Listener を implements しなくても、アノテーションを付けるだけでその辺を勝手によしなにしてくれますし、findViewById の手間も @InjectView で解決できます。
さて、ListView
にはOnItemClickListener
という、ListView
の中のアイテムをクリックした時のハンドラがあります。
ButterKnife を用いると、以下のようにサクッと書くことが出来るようになりますね。
public class ListViewActivity extends Activity {
@InjectView(R.id.listview)
ListView listview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listview);
ButterKnife.inject(this);
// adapter をセットする
}
@OnItemClick(R.id.listview)
public void onListItemClick(int position) {
Log.v("ListView", "selected pos=" + position);
}
}
onItemClick
メソッドの引数は、AdapterView.OnItemClickListener#onItemClick(AdapterView<?>, View, int, long)
の引数の全部またはどれか好きなものを指定できます。おそらく最もよく使うのは、どのposition
のものを選択したかを判定したいので、第三引数のposition
なのではないでしょうか。今回もそれを使っています。
ListView
のアイテムをクリックすると、選択した位置がログに出力されるはずです。
ここで、以下のような実装をしてみたとします。
public class ListViewActivity extends Activity {
@InjectView(R.id.listview)
ListView listview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listview);
ButterKnife.inject(this);
// ListView にヘッダを追加する
TextView label = new TextView(this);
label.setText("List header!");
listview.addHeaderView(label);
// adapter をセットする
}
@OnItemClick(R.id.listview)
public void onListItemClick(int position) {
Log.v("ListView", "selected pos=" + position);
}
}
ヘッダに View を追加しました。簡単ですね。
ListView
のアイテムをクリックすると、選択した位置がログに出力されるはずです。ヘッダもクリック出来るようになっていると思います。
が、よく見てみると、position
がヘッダを起点に0
からカウントされていることがわかると思います。
つまり、セットしたListAdapter
のアイテムのposition
ではなく、追加したヘッダの分だけずれたposition
が引数に渡されてきます。
ヘッダやフッタをListView
にセットした場合、最初にListView#setAdapter(ListAdapter)
でセットしたListAdapter
ではない別のアダプタが内部的に使用されます。つまり、ListView#getAdapter()
をすると、その内部的に使用されているアダプタが返ってくることになります。これはHeaderViewListAdapter
と呼ばれ、最初にセットしたListAdapter
はこのHeaderViewListAdapter
が内部に保持しています。よって、position
から正しい(ListAdapter
が持つアイテムの0
から始まる)位置を知るには、以下のような実装をしてあげないといけません。
public class ListViewActivity extends Activity {
@InjectView(R.id.listview)
ListView listview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listview);
ButterKnife.inject(this);
// ListView にヘッダを追加する
TextView label = new TextView(this);
label.setText("List header!");
listview.addHeaderView(label);
// adapter をセットする
}
@OnItemClick(R.id.listview)
public void onListItemClick(int position) {
//ヘッダやフッタを追加した ListView から getAdapter するときは必ずこうする
HeaderViewListAdapter wrapper = (HeaderViewListAdapter) listview.getAdapter();
// 正しい位置を計算するため、ヘッダの数だけ position がズレるので引き算する
int itemPosition = position - wrapper.getHeadersCount();
Log.v("ListView", "selected pos=" + itemPosition);
// ズレた分の引き算で itemPosition が負数になる場合は position がヘッダの位置だったと分かる
if (itemPosition < 0) {
return;
}
// 選択されたアイテムを引くときはこれを使う
ListAdapter wrapped = wrapper.getWrappedAdapter();
Object selectedItem = wrapped.getItem(itemPosition);
}
}
勿論、フッタがある場合はそれも考慮する必要がありますし、両方あるときはそれに応じて計算が必要です。