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

android, android-bluetooth, android-recyclerview, android-viewholder

Solution

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.

Problem

In a simple app project at GitHub I have only 2 custom Java-files: - MainActivity.java contains Bluetooth- and UI-related source code - DeviceListAdapter.java contains an `Adapter` and `ViewHolder` for displaying Bluetooth devices in a `RecyclerView` 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 } } ```

Original source