问题
i am creating an app that is supposed to display bluetooth devices in a recyclerview and i want a user to be able to click the items to perform an action. for now i am just attempting to make a toast appear on click but later i would like to possibly display a dialog giving choices to pair, etc. however i am apperently missing something in my usage of onclicklistener. i am attempting to have my ViewHolder class: DeviceHolder implement View.OnClickListener and placing the call to Toast.makeText() inside of my onClick override. however, nothing is happening. i am sure i am just missing something minor and would appreciate help finding the problem. also i am doing this in kotlin which i am new to and if there is possibly a more efficient, kotlin type of way to do this that would also be of help. i am posting my code below. thanks in advance.
class DeviceAdapter(val mContext : Context) : RecyclerView.Adapter<DeviceAdapter.DeviceHolder>(){
val mDevices = ArrayList<BluetoothDevice>()
interface OnClickListener{
fun onClick(v: View)
}
fun updateItems(list: ArrayList<BluetoothDevice>){
mDevices.clear()
mDevices.addAll(list)
Log.d(TAG, "updating items : $mDevices")
notifyDataSetChanged()
}
fun ViewGroup.inflate(@LayoutRes res: Int, attachToRoot: Boolean = false): View{
return LayoutInflater.from(mContext).inflate(res, this, attachToRoot)
}
override fun onBindViewHolder(holder: DeviceHolder, position: Int) {
Log.d(TAG, "onBindViewHolder called!")
holder.bindItems(mDevices.get(position))
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): DeviceAdapter.DeviceHolder{
Log.d(TAG, "onCreateViewHolder called!")
val v = parent!!.inflate(R.layout.device_item, false)
return DeviceHolder(v)
}
override fun getItemCount(): Int {
return mDevices.size
}
inner class DeviceHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
override fun onClick(v: View?) {
Toast.makeText(mContext, "test", Toast.LENGTH_LONG).show()
}
val nameView = itemView.findViewById(R.id.nameView) as TextView
val addrView = itemView.findViewById(R.id.addressView) as TextView
fun bindItems(btDevice: BluetoothDevice) {
Log.d(TAG, "holder created!")
nameView.text = btDevice.name
addrView.text = btDevice.address
itemView.setOnClickListener { this }
}
}
companion object {
val TAG = "Device Adapter"
}
}
here are the log messages:
10-09 00:35:50.233 7581-7581/com.example.zemcd.toofxchange D/DiscoveryTask: device found!
10-09 00:35:51.795 7581-7581/com.example.zemcd.toofxchange D/DiscoveryTask: device found!
10-09 00:35:56.752 7581-7581/com.example.zemcd.toofxchange D/DiscoveryTask: device list : [**:B8:9A:39:1D:**, **:DF:BF:2A:F3:**]
10-09 00:35:56.752 7581-7581/com.example.zemcd.toofxchange D/Device Adapter: updating items : [**:B8:9A:39:1D:**, **:DF:BF:2A:F3:**]
10-09 00:35:56.752 7581-7581/com.example.zemcd.toofxchange D/DiscoveryTask: discovery finished
10-09 00:35:56.762 7581-7581/com.example.zemcd.toofxchange D/Device Adapter: onCreateViewHolder called!
10-09 00:35:56.774 7581-7581/com.example.zemcd.toofxchange D/Device Adapter: onBindViewHolder called!
10-09 00:35:56.774 7581-7581/com.example.zemcd.toofxchange D/Device Adapter: holder created!
10-09 00:35:56.783 7581-7581/com.example.zemcd.toofxchange D/Device Adapter: onCreateViewHolder called!
10-09 00:35:56.786 7581-7581/com.example.zemcd.toofxchange D/Device Adapter: onBindViewHolder called!
10-09 00:35:56.786 7581-7581/com.example.zemcd.toofxchange D/Device Adapter: holder created!
回答1:
i managed to get this fixed by eliminating the implementation of OnClickListener and just using this statement instead itemView.setOnClickListener { /* lamda here */ }. i don't believe this exact syntax is available in java but in kotlin in works. here is my revised code :
inner class DeviceHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val nameView = itemView.findViewById(R.id.nameView) as TextView
val addrView = itemView.findViewById(R.id.addressView) as TextView
fun bindItems(btDevice: BluetoothDevice) {
Log.d(TAG, "holder created!")
nameView.text = btDevice.name
addrView.text = btDevice.address
itemView.setOnClickListener { Toast.makeText(it.context, "testing", Toast.LENGTH_SHORT).show() }
}
}
by using the it keyword inside of the lambda i accessed the context of the itemView object, instead of using my original mContext described above. i am still unsure of exactly why my old approach did not work, but this did so that works for me.
回答2:
This works:
1 set your own click listener to the adapter, and keep a reference to it:
private OnItemClickListener onItemClickListener;
// ...
public interface OnItemClickListener {
// note: here you would need some params, for instance the view
void onItemClick(View v);
}
public void setOnItemClickListener(OnItemClickListener l) {
this.onItemClickListener = l;
}
2 when each viewholder is created, set a listener to it, and once called back, pass the callback to the listener set in 1:
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ViewHolder vh = getViewHolder( /*...*/ );
vh.setOnItemClickListener(new OnItemClickListener() {
if (onItemClickListener != null) {
// each view holder calls the one listener set to the Adapter
onItemClickListener.onItemClick(v);
}
}
}
3 make your viewholder to listen to the (holded) view onClick, and let it forward the call:
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
// each view holder has a listener set to it
private OnItemClickListener mListener;
public ViewHolder(View v) {
super(v);
// ...
// set the viewholder as a listener to the view's clicks
v.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// when view is clicked, simply forward the call
if (mListener != null) {
mListener.onItemClick(v);
}
}
public void setOnItemClickListener(OnItemClickListener listener) {
mListener = listener;
}
}
4 at this point, the adapter user will have:
mAdapter = new MyAdapter(ctx);
mAdapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() {
@Override
public void onItemClick(View v) {
// the given view was clicked, do something with it
}
});
回答3:
According to your code you are implementing the clicklistner but haven't added any register to click listener.add the clickListener for nameView and AddrView.
nameView.setOnClickListener(this);
addrView.setOnClickListener(this);
来源:https://stackoverflow.com/questions/46640897/how-to-implement-onclicklistener-for-custom-viewholder-in-my-recycler-view