如何从 RecyclerView.Adapter 中的 ViewHolder 调用 MainActivity 方法?
How to call a MainActivity method from ViewHolder in RecyclerView.Adapter?
在 a simple app project at GitHub 我只有 2 个自定义 Java-files:
- MainActivity.java 包含蓝牙和 UI 相关的源代码
- DeviceListAdapter.java 包含
Adapter
和 ViewHolder
用于在 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();
}
并且在 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();
}
}
我的问题:
如何从 ViewHolder
的 onClick
方法中调用 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;
}
然后在内部,在您的 ViewHolder
的 onClick()
:
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);
在 a simple app project at GitHub 我只有 2 个自定义 Java-files:
- MainActivity.java 包含蓝牙和 UI 相关的源代码
- DeviceListAdapter.java 包含
Adapter
和ViewHolder
用于在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();
}
并且在 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();
}
}
我的问题:
如何从 ViewHolder
的 onClick
方法中调用 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;
}
然后在内部,在您的 ViewHolder
的 onClick()
:
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);