tutorial - recyclerview android 3
¿Cómo llamar a un método MainActivity desde ViewHolder en RecyclerView.Adapter? (5)
En su adaptador, cree una interfaz que proporcionará una devolución de llamada a la actividad principal
public interface MyCallback{
void onItemClicked();
}
private MyCallback listener;
public setOnItemClickListener(MyCallback callback){
listener = callback;
}
haz que tu actividad principal lo implemente
public class MainActivity extends AppCompatActivity implements MyCallback
luego complete la devolución de llamada
@Override
public void onItemClick(){
//do work
}
luego simplemente configura la devolución de llamada desde el adaptador
mDeviceListAdapter.setOnItemClickListener(this);
En un proyecto de aplicación simple en GitHub tengo solo 2 archivos personalizados de Java:
- MainActivity.java contiene código fuente relacionado con Bluetooth y UI
- DeviceListAdapter.java contiene un
Adapter
yViewHolder
para mostrar dispositivos Bluetooth en unRecyclerView
MainActivity.java contiene un método para llamar, cuando el usuario toca un dispositivo Bluetooth en 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();
}
Y en la clase ViewHolder
(en DeviceListAdapter.java ) se define el oyente click:
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();
}
}
Mi problema:
¿Cómo llamar al método confirmConnection(address)
método ViewHolder
s onClick
?
Sigo moviendo la declaración de la clase ViewHolder
entre los 2 archivos Java y también intenté ponerla en su propio archivo, y simplemente no puedo encontrar el camino correcto.
¿Debo quizás agregar un campo a la clase ViewHolder
y (cuando?) Almacenar una referencia a la instancia MainActivity
allí?
ACTUALIZAR:
Esto funciona para mí, pero parece ser una solución alternativa (y también estaba pensando en utilizar LocalBroadcastReceiver
, lo que sería una solución aún más hackish) -
@Override
public void onClick(View v) {
String address = deviceAddress.getText().toString();
try {
((MainActivity) v.getContext()).confirmConnection(address);
} catch (Exception e) {
// ignore
}
}
Para mantener sus clases desacopladas, sugiero que defina una interfaz en su adaptador, algo como:
public interface OnBluetoothDeviceClickedListener {
void onBluetoothDeviceClicked(String deviceAddress);
}
Luego agrega un setter para esto en tu adaptador:
private OnBluetoothDeviceClickedListener mBluetoothClickListener;
public void setOnBluetoothDeviceClickedListener(OnBluetoothDeviceClickedListener l) {
mBluetoothClickListener = l;
}
Luego internamente, en ViewHolder
onClick()
:
if (mBluetoothClickListener != null) {
final String addresss = deviceAddress.getText().toString();
mBluetoothClickListener.onBluetoothDeviceClicked(address);
}
Luego solo pase su MainActivity
en un detector del Adapter
:
mDeviceListAdapter.setOnBluetoothDeviceClickedListener(new OnBluetoothDeviceClickedListener() {
@Override
public void onBluetoothDeviceClicked(String deviceAddress) {
confirmConnection(deviceAddress);
}
});
De esta forma, puede reutilizar el adaptador más tarde sin que esté vinculado a ese comportamiento en particular.
Puede llamar al método de Actividad usando una instancia de Actividad como esta, dentro de MainActivity escriba debajo del código
mDeviceListAdapter = new DeviceListAdapter(MainActivity.this);
Adaptador interior
private MainActivity _mainActivity;
public DeviceListAdapter(MainActivity activity){
this._mainActivity=activity;
}
Dentro de tu método onClick
_mainActivity.yourActivityMethod(address);
Puede pasar la actividad principal como un parámetro constructor para el adaptador y almacenarlo en un campo. O utiliza un bus de eventos, hay múltiples formas de hacerlo, yo iría por el campo
Para aquellos que buscan invocar una devolución de llamada desde un ViewHolder estático, hagan lo siguiente. Dejarte tener un adaptador:
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);
}
}
Luego debe agregar un método setCallback y llamarlo desde activity / fragment. Además, no debe hacer una devolución de llamada estática (puede causar problemas cuando utiliza el mismo adaptador en muchas clases). Debe crear un campo dentro de ViewHolder. Asi que:
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());
}
}
}
};
}
}
Después de que haya hecho el adaptador, puede invocarlo así:
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);