如何从 RecyclerView.Adapter 中的 ViewHolder 调用 MainActivity 方法?

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

a simple app project at GitHub 我只有 2 个自定义 Java-files:

  1. MainActivity.java 包含蓝牙和 UI 相关的源代码
  2. DeviceListAdapter.java 包含 AdapterViewHolder 用于在 RecyclerView
  3. 中显示蓝牙设备

MainActivity.java 包含一个方法,当用户点击 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();
}

并且在 ViewHolder class(在 DeviceListAdapter.java)中定义了点击侦听器:

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();
    }
  }

我的问题:

如何从 ViewHolderonClick 方法中调用 confirmConnection(address) 方法?

我一直在 2 个 Java 文件之间移动 ViewHolder class 声明,也尝试将其放入自己的文件中 - 但找不到正确的方法。

我是否应该向 ViewHolder class 添加一个字段并(何时?)在那里存储对 MainActivity 实例的引用?

更新:

这对我有用,但似乎是一种解决方法(而且我也在考虑使用 LocalBroadcastReceiver - 这将是一个更骇人听闻的解决方法)-

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

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

您可以将 MainActivity 作为适配器的构造函数参数传递,并将其存储在字段中。或者你使用事件总线 - 有多种方法可以做到 - 我会去现场

为了让您的 类 解耦,我建议在您的适配器上定义一个接口,例如:

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

然后在您的适配器中为此添加一个 setter:

private OnBluetoothDeviceClickedListener mBluetoothClickListener;

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

然后在内部,在您的 ViewHolderonClick():

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

然后让您的 MainActivity 将侦听器传递给 Adapter:

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

这样您以后就可以重用适配器,而不会绑定到特定行为。

在您的适配器中,创建一个接口,该接口将向主 activity

提供回调
public interface MyCallback{
    void onItemClicked();
}

private MyCallback listener;

public setOnItemClickListener(MyCallback callback){
    listener = callback;
}

让你的主要 activity 实施它。

public class MainActivity extends AppCompatActivity implements MyCallback

然后实现回调

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

然后只需设置来自适配器的回调

mDeviceListAdapter.setOnItemClickListener(this);

你可以像这样使用Activity的实例调用Activity方法,在MainActivity里面写下面的代码

mDeviceListAdapter = new DeviceListAdapter(MainActivity.this);

内部适配器

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

在您的 onClick 方法中

 _mainActivity.yourActivityMethod(address);

对于那些正在寻找从静态 ViewHolder 调用回调的人,请执行以下操作。 让你有个适配器:

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);
    }
}

然后您应该添加一个 setCallback 方法并从 activity/fragment 调用它。此外,您不应将回调设为静态(当您在许多 类 中使用相同的适配器时可能会导致问题)。您应该在 ViewHolder 中创建一个字段。所以:

    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's 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());
                    }
                }
            }
        };
    }
}

创建适配器后,您可以这样调用它:

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);