Alexander Farber Alexander Farber - 24 days ago 15
Android Question

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

In a simple app project at GitHub I have only 2 custom Java-files:


  1. MainActivity.java contains Bluetooth- and UI-related source code

  2. DeviceListAdapter.java contains an
    Adapter
    and
    ViewHolder
    for displaying Bluetooth devices in a
    RecyclerView



app screenshot

The MainActivity.java contains a method to be called, when user taps on a Bluetooth device in the
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 the DeviceListAdapter.java) the click listener 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
s
onClick
method?

I keep moving
ViewHolder
class declaration between the 2 Java files and also tried putting it into its own file - and just can't find the right way.

Should I maybe add a field to
ViewHolder
class and (when?) store a reference to
MainActivity
instance there?

UPDATE:

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

@Override
public void onClick(View v) {
String address = deviceAddress.getText().toString();

try {
((MainActivity) v.getContext()).confirmConnection(address);
} catch (Exception e) {
// ignore
}
}

Answer

To keep your classes decoupled, I'd suggest defining an interface on your adapter, something like:

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

Then add a setter for this in your adapter:

private OnBluetoothDeviceClickedListener mBluetoothClickListener;

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

Then internally, in your ViewHolder's onClick():

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

Then just have your MainActivity pass in a listener to the Adapter:

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

This way you can reuse the adapter later without it being tied to that particular behavior.