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

时间:2015-09-22 15:17:37

标签: android android-recyclerview android-bluetooth android-viewholder

a simple app project at GitHub中,我只有2个自定义Java文件:

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

    app screenshot

    当用户点击RecyclerView中的蓝牙设备时,MainActivity.java包含要调用的方法:

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

    我的问题:

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

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

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

    更新

    这对我有用,但似乎是一种解决方法(而且我还在考虑使用LocalBroadcastReceiver - 这将是一种更加愚蠢的解决方法) -

        @Override
        public void onClick(View v) {
            String address = deviceAddress.getText().toString();
    
            try {
                ((MainActivity) v.getContext()).confirmConnection(address);
            } catch (Exception e) {
                // ignore
            }
        }
    

5 个答案:

答案 0 :(得分:25)

为了让你的类分离,我建议你在你的适配器上定义一个接口,例如:

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

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

private OnBluetoothDeviceClickedListener mBluetoothClickListener;

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

然后在内部,ViewHolder的{​​{1}}:

onClick()

然后让if (mBluetoothClickListener != null) { final String addresss = deviceAddress.getText().toString(); mBluetoothClickListener.onBluetoothDeviceClicked(address); } 传递给MainActivity

的听众
Adapter

通过这种方式,您可以稍后重用适配器,而不必将其与特定行为联系起来。

答案 1 :(得分:5)

对于那些正在寻找从静态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);

答案 2 :(得分:2)

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

答案 3 :(得分:2)

在适配器中创建一个接口,该接口将提供对主活动的回调

public interface MyCallback{
    void onItemClicked();
}

private MyCallback listener;

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

让您的主要活动实现它

public class MainActivity extends AppCompatActivity implements MyCallback

然后实现回调

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

然后只需从适配器设置回调

mDeviceListAdapter.setOnItemClickListener(this);

答案 4 :(得分:1)

您可以使用这样的Activity实例调用Activity方法,在MainActivity中写下代码

mDeviceListAdapter = new DeviceListAdapter(MainActivity.this);

内部适配器

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

在onClick方法中

 _mainActivity.yourActivityMethod(address);