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

Ale*_*ber 11 android android-bluetooth android-viewholder android-recyclerview

GitHub的一个简单的应用程序项目中,我只有2个自定义Java文件:

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

应用截图

当用户点击蓝牙设备时,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();
}
Run Code Online (Sandbox Code Playgroud)

ViewHolder类(在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();
    }
  }
Run Code Online (Sandbox Code Playgroud)

我的问题:

如何confirmConnection(address)ViewHolders onClick方法调用方法?

我继续ViewHolder在2个Java文件之间移动类声明,并尝试将其放入自己的文件中 - 并且找不到正确的方法.

我是否应该在ViewHolder课堂上添加一个字段?(当?)存储对MainActivity实例的引用?

更新:

这对我有用,但似乎是一种解决方法(我也在考虑使用LocalBroadcastReceiver- 这将是一个更加黑客的解决方法) -

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

        try {
            ((MainActivity) v.getContext()).confirmConnection(address);
        } catch (Exception e) {
            // ignore
        }
    }
Run Code Online (Sandbox Code Playgroud)

kco*_*ock 27

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

public interface OnBluetoothDeviceClickedListener {
    void onBluetoothDeviceClicked(String deviceAddress);
}
Run Code Online (Sandbox Code Playgroud)

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

private OnBluetoothDeviceClickedListener mBluetoothClickListener;

public void setOnBluetoothDeviceClickedListener(OnBluetoothDeviceClickedListener l) {
    mBluetoothClickListener = l;
}
Run Code Online (Sandbox Code Playgroud)

然后在内部,在你ViewHolderonClick():

if (mBluetoothClickListener != null) {
    final String addresss = deviceAddress.getText().toString();
    mBluetoothClickListener.onBluetoothDeviceClicked(address);
}
Run Code Online (Sandbox Code Playgroud)

然后让你的MainActivity传球听众Adapter:

mDeviceListAdapter.setOnBluetoothDeviceClickedListener(new OnBluetoothDeviceClickedListener() {
    @Override
    public void onBluetoothDeviceClicked(String deviceAddress) {
        confirmConnection(deviceAddress);
    }
});
Run Code Online (Sandbox Code Playgroud)

这样,您可以稍后重用适配器,而不必将其绑定到该特定行为.

  • 如果我收到"非静态方法无法从静态上下文引用"错误怎么办? (2认同)
  • 如何在静态ViewHolder类中使用mBluetoothClickListener非静态字段? (2认同)

Coo*_*ind 6

对于那些正在寻找从静态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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您应该添加一个setCallback方法并从活动/片段中调用它。另外,您不应该使回调静态化(在许多类中使用相同的适配器时,可能会导致问题)。您应该在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());
                    }
                }
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

制作适配器后,可以调用它:

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);
Run Code Online (Sandbox Code Playgroud)