ListViewでは、setOnItemClickListener()でアイテムがクリックされたときのイベントを拾うことができた。RcyclerViewはどうかというと、残念ながらそういう仕組みが備わってはいない。なのでクリックイベントを拾いたい場合は自分で実装しなければならない。
RcyclerViewのクリックイベントは、Adapterで生成したViewにView.OnClickListenerを設定すればOKだ。
方法は二つある。
AdapterのonCreateViewHolder()で実装
ViewHolderで保持するViewにOnClickListenerを設定する方法。
class SampleRecyclerAdapter extends RecyclerView.Adapter<SampleRecyclerAdapter.SampleViewHolder> {
//メンバ変数
String[] itemDatas; //リストのデータを保持する変数
//コンストラクタ
SampleRecyclerAdapter(String[] itemDatas) {
this.itemDatas = itemDatas;
}
@NonNull
@Override
public SampleViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
//onCreateViewHolder()ではリスト一行分のレイアウトViewを生成する
//作成したViewはViewHolderに格納してViewHolderをreturnで返す
//レイアウトXMLからViewを生成
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.listitem_recyclerview1, viewGroup, false);
//ViewHolderを生成
final SampleViewHolder holder = new SampleViewHolder(view);
//クリックイベントを登録
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
Toast.makeText(v.getContext(), itemDatas[position], Toast.LENGTH_SHORT).show();
}
});
return holder;
}
@Override
public void onBindViewHolder(@NonNull final SampleViewHolder sampleViewHolder, int position) {
//onBindViewHolder()ではデータとレイアウトの紐づけを行なう
sampleViewHolder.text01.setText(itemDatas[position]);
}
@Override
public int getItemCount() {
//データ個数を返す
return itemDatas.length;
}
/* ViewHolder(インナークラス) */
class SampleViewHolder extends RecyclerView.ViewHolder {
TextView text01;
SampleViewHolder(@NonNull View itemView) {
super(itemView);
text01 = itemView.findViewById(R.id.text01);
}
}
}
AdapterのonBindViewHolder()で実装
各アイテムのViewが実際に生成されるたびにOnClickListenerを設定する方法。
class SampleRecyclerAdapter extends RecyclerView.Adapter<SampleRecyclerAdapter.SampleViewHolder> {
//メンバ変数
String[] itemDatas; //リストのデータを保持する変数
//コンストラクタ
SampleRecyclerAdapter(String[] itemDatas) {
this.itemDatas = itemDatas;
}
@NonNull
@Override
public SampleViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
//レイアウトXMLからViewを生成
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.listitem_recyclerview1, viewGroup, false);
//ViewHolderを生成
final SampleViewHolder holder = new SampleViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(@NonNull final SampleViewHolder sampleViewHolder, int position) {
//onBindViewHolder()ではデータとレイアウトの紐づけを行なう
sampleViewHolder.text01.setText(itemDatas[position]);
//クリックイベントを登録
final int p = position;
sampleViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(v.getContext(), itemDatas[p], Toast.LENGTH_SHORT).show();
}
});
}
@Override
public int getItemCount() {
//データ個数を返す
return itemDatas.length;
}
/* ViewHolder(インナークラス) */
class SampleViewHolder extends RecyclerView.ViewHolder {
TextView text01;
SampleViewHolder(@NonNull View itemView) {
super(itemView);
text01 = itemView.findViewById(R.id.text01);
}
}
}
実行結果
どちらの方法も実行結果は同じになる。
イベント処理本体をAdapterの外に出す
しかし、Adapterの内部にクリック時の処理を書いてしまうのはなんとなくモヤモヤした気分になる。Adapter本来の機能ではないコードがAdapter内部に居座って可読性が悪くなってしまうからだ。できるならクリックイベントの中身はコールバック等でAdapterの外に出してしまうのが良策だと思う。
例えばこんな感じに↓
public class MainActivity extends AppCompatActivity {
//リストに表示するデータ
private final static String[] listDatas = {
"ONE","TWO","THREE","FOUR",
"FIVE","SIX","SEVEN","EIGHT",
"NINE","TEN","ELEVEN","TWELVE",
"THIRTEEN","FOURTEEN","FIFTEEN","SIXTEEN",
"SEVENTEEN","EIGHTEEN","NINETEEN","TWENTY" };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final RecyclerView recyclerView = findViewById(R.id.recyclerview1);
//LayoutManagerを設定
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL); //縦方向に設定
recyclerView.setLayoutManager(layoutManager);
//ItemDecorationを設定
DividerItemDecoration itemDecoration = new DividerItemDecoration(this, layoutManager.getOrientation());
recyclerView.addItemDecoration(itemDecoration);
//アダプタのセット
final SampleRecyclerAdapter adapter = new SampleRecyclerAdapter(listDatas){
// onItemClick()をオーバーライドして
// クリックイベントの処理を記述する
@Override
void onItemClick(View view, int position, String itemData) {
Toast.makeText(view.getContext(),itemData, Toast.LENGTH_SHORT).show();
}
};
recyclerView.setAdapter(adapter);
}
/* -------------------------------------------------------
* RecyclerViewのアダプタ
* --------------------------------------------------------*/
class SampleRecyclerAdapter extends RecyclerView.Adapter<SampleRecyclerAdapter.SampleViewHolder> {
//メンバ変数
String[] itemDatas; //リストのデータを保持する変数
//コンストラクタ
SampleRecyclerAdapter(String[] itemDatas) {
this.itemDatas = itemDatas;
}
@NonNull
@Override
public SampleViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
//レイアウトXMLからViewを生成
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.listitem_recyclerview1, viewGroup, false);
//ViewHolderを生成
final SampleViewHolder holder = new SampleViewHolder(view);
//クリックイベントを登録
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
//処理はonItemClick()に丸投げ
onItemClick(v, position, itemDatas[position]);
}
});
return holder;
}
@Override
public void onBindViewHolder(@NonNull final SampleViewHolder sampleViewHolder, int position) {
//onBindViewHolder()ではデータとレイアウトの紐づけを行なう
sampleViewHolder.text01.setText(itemDatas[position]);
}
@Override
public int getItemCount() {
//データ個数を返す
return itemDatas.length;
}
void onItemClick(View view, int position, String itemData) {
//アダプタのインスタンスを作る際
//このメソッドをオーバーライドして
//クリックイベントの処理を設定する
}
/* ViewHolder(インナークラス) */
class SampleViewHolder extends RecyclerView.ViewHolder {
TextView text01;
SampleViewHolder(@NonNull View itemView) {
super(itemView);
text01 = itemView.findViewById(R.id.text01);
}
}
}
}