How to call MainActivity method from ViewHolder in RecyclerView.Adapter?

In a simple application project on GitHub , I have only 2 custom Java files:

application screenshot

MainActivity.java contains a method that will be called when a user clicks on a Bluetooth device in RecyclerView :

 public void confirmConnection(String address) { final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage("Do you want to pair to " + device + "?"); builder.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { device.createBond(); } }); builder.setNegativeButton(R.string.button_cancel, null); builder.show(); } 

And in the ViewHolder class (in DeviceListAdapter.java ) the receiver of clicks is defined:

 public class DeviceListAdapter extends RecyclerView.Adapter<DeviceListAdapter.ViewHolder> { private ArrayList<BluetoothDevice> mDevices = new ArrayList<BluetoothDevice>(); protected static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { private TextView deviceAddress; public ViewHolder(View v) { super(v); v.setOnClickListener(this); } @Override public void onClick(View v) { String address = deviceAddress.getText().toString(); Toast.makeText(v.getContext(), "How to call MainActivity.confirmConnection(address)?", Toast.LENGTH_SHORT).show(); } } 

My problem:

How to call confirmConnection(address) method from ViewHolder onClick method?

I keep moving the ViewHolder class ViewHolder between the two Java files, and also try to put it in my own file - and just can't find the right path.

Should I add a field to the ViewHolder class and (when?) Store a reference to the MainActivity instance?

UPDATE:

This works for me, but it seems to be a workaround (and also I was thinking of using LocalBroadcastReceiver - which would be an even more hacker workaround) -

  @Override public void onClick(View v) { String address = deviceAddress.getText().toString(); try { ((MainActivity) v.getContext()).confirmConnection(address); } catch (Exception e) { // ignore } } 
+10
android android-recyclerview android-viewholder android-bluetooth
source share
5 answers

For your classes to be decoupled, I would suggest defining an interface on your adapter, for example:

 public interface OnBluetoothDeviceClickedListener { void onBluetoothDeviceClicked(String deviceAddress); } 

Then add for this adapter to the adapter:

 private OnBluetoothDeviceClickedListener mBluetoothClickListener; public void setOnBluetoothDeviceClickedListener(OnBluetoothDeviceClickedListener l) { mBluetoothClickListener = l; } 

Then internally, in ViewHolder onClick() :

 if (mBluetoothClickListener != null) { final String addresss = deviceAddress.getText().toString(); mBluetoothClickListener.onBluetoothDeviceClicked(address); } 

Then just pass MainActivity to the listener in the Adapter :

 mDeviceListAdapter.setOnBluetoothDeviceClickedListener(new OnBluetoothDeviceClickedListener() { @Override public void onBluetoothDeviceClicked(String deviceAddress) { confirmConnection(deviceAddress); } }); 

This way, you can reuse the adapter later without being tied to this particular behavior.

+25
source share

For those looking for a callback call from a static ViewHolder object, follow these steps: Suppose you have an adapter:

 public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { private final int resource; private final List<Item> items; private final LayoutInflater inflater; ... private Callback callback; private static class ViewHolder extends RecyclerView.ViewHolder { ... } public interface Callback { void onImageClick(int position); void onRemoveItem(int position); } } 

Then you have to add the setCallback method and call it from the action / fragment. Also, you should not put a callback (this can lead to problems when using the same adapter in many classes). You must create a field inside the ViewHolder. So:

  public MyAdapter(Context context, int resource, List<Item> items, Callback callback) { super(); this.resource = resource; this.items = items; this.inflater = LayoutInflater.from(context); this.callback = callback; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { final ViewHolder viewHolder = (ViewHolder) holder; final Item item = this.items.get(position); viewHolder.caption.setText(item.caption); viewHolder.callback = callback; } // A method to set a callback from activity/fragment. public void setCallback(Callback callback) { this.callback = callback; } public static class Item { public long id; public String number; public String caption; ... } private static class ViewHolder extends RecyclerView.ViewHolder { protected final TextView caption; // A reference to an adapter callback. protected Callback callback; public ViewHolder(View view) { super(view); this.caption = (TextView) view.findViewById(R.id.caption); } private View.OnClickListener onClickListener = new View.OnClickListener() { @Override public void onClick(View v) { int id = v.getId(); if (id == R.id.image) { // Invoke the callback here. if (callback != null) { callback.onImageClick(getLayoutPosition()); } } } }; } } 

After you have made the adapter, you can call it like this:

 adapter = new MyAdapter(getActivity(), R.layout.item, new ArrayList<MyAdapter.Item>(), null); adapterListener = new MyAdapter.Callback() { @Override public void onImageClick(int position) { // Some actions. } @Override public void onRemoveItem(int position) { // Some actions. } }; adapter.setCallback(adapterListener); 
+5
source share

You can pass MainActivity as a constructor parameter for the adapter and store it in a field. Or you use an event bus - there are several ways to do this - I would go to the field

+2
source share

In your adapter, create an interface that provides a callback to the main activity

 public interface MyCallback{ void onItemClicked(); } private MyCallback listener; public setOnItemClickListener(MyCallback callback){ listener = callback; } 

Make your core business do this.

 public class MainActivity extends AppCompatActivity implements MyCallback 

then implement callback

 @Override public void onItemClick(){ //do work } 

then just set the callback from the adapter

 mDeviceListAdapter.setOnItemClickListener(this); 
+2
source share

You can call the Activity method using an Activity instance like this, inside MainActivity write the code below

 mDeviceListAdapter = new DeviceListAdapter(MainActivity.this); 

Internal adapter

  private MainActivity _mainActivity; public DeviceListAdapter(MainActivity activity){ this._mainActivity=activity; } 

Inside the onClick method

  _mainActivity.yourActivityMethod(address); 
+1
source share

All Articles