8

In a simple app project at GitHub ho solo 2 personalizzati Java-files:Come chiamare un metodo MainActivity da ViewHolder in RecyclerView.Adapter?

  1. MainActivity.java contiene Bluetooth e il codice sorgente UI legati
  2. DeviceListAdapter.java contiene un Adapter e ViewHolder per la visualizzazione di dispositivi Bluetooth in un RecyclerView

app screenshot

MainActivity.java contiene un metodo da chiamare, quando l'utente tocca su un dispositivo Bluetooth nella 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(); 
} 

E nella classe ViewHolder (nel DeviceListAdapter.java) il clic listener è definita:

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

mio problema:

Come chiamare confirmConnection(address) metodo dal ViewHolder s metodo onClick?

Continuo a spostare la dichiarazione di classe ViewHolder tra i 2 file Java e ho anche provato a inserirla nel proprio file - e proprio non riesco a trovare la strada giusta.

Devo forse aggiungere un campo alla classe ViewHolder e (quando?) Memorizzare un riferimento all'istanza MainActivity lì?

UPDATE:

Questo funziona per me, ma sembra essere una soluzione alternativa (e anche io pensavo di usare LocalBroadcastReceiver - che sarebbe un soluzione ancora più hackish) -

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

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

tuo ViewHolder contiene una visualizzazione. Questa vista ha un contesto. Questo Context è molto probabilmente la tua attività. Quindi lancia il contesto a MainActivity e dovresti stare bene. L'utilizzo delle interfacce è l'approccio migliore. ((MainActivity) v.getContext()). ConfirmConnection() dovrebbe esserlo. – ElDuderino

risposta

18

Per mantenere le classi disaccoppiato, io suggerirei che definisce un'interfaccia sulla scheda, qualcosa di simile:

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

Quindi aggiungere un setter per questo l'adattatore:

private OnBluetoothDeviceClickedListener mBluetoothClickListener; 

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

Poi internamente, in S' onClick() tuoi ViewHolder:

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

Poi basta avere il pass MainActivity in un ascoltatore al Adapter:

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

In questo modo è possibile riutilizzare l'adattatore in seguito senza che sia legato a quel particolare comportamento.

+2

Cosa succede se viene visualizzato un errore "Impossibile trovare il metodo non statico da un contesto statico"? – user3484582

+0

@ user3484582 Probabilmente significa che stai cercando di chiamare un metodo nella classe che racchiude da una classe interna statica (ad esempio se hai una classe interna statica nella tua attività e stai provando a chiamare un metodo sulla tua attività da lì) – kcoppock

+2

Come può mBluetoothClickListener campo non statico essere utilizzato nella classe ViewHolder statico? – MBDevelop

1

È possibile passare MainActivity come parametro costruttore per l'adapter e memorizzarlo in un campo. O si utilizza un evento-bus - ci sono diversi modi per farlo - vorrei andare per il campo

1

Nella scheda di creare un'interfaccia che fornirà un callback per l'attività principale

public interface MyCallback{ 
    void onItemClicked(); 
} 

private MyCallback listener; 

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

avere la vostra attività principale attuarlo

public class MainActivity extends AppCompatActivity implements MyCallback 

poi omplement il callback

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

quindi basta impostare la richiamata dall'adattatore

mDeviceListAdapter.setOnItemClickListener(this); 
1

È possibile chiamare il metodo di attività utilizzando un'istanza di attività di questo tipo, all'interno MainActivity scrivere sottostante Codice

mDeviceListAdapter = new DeviceListAdapter(MainActivity.this); 

All'interno adattatore

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

Dentro il metodo onClick

_mainActivity.yourActivityMethod(address); 
3

Per coloro che sono looki ng per invocare una richiamata da una ViewHolder statica procedi come segue. ti permettono di avere un adattatore:

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

allora si dovrebbe aggiungere un metodo setCallback e chiamare da attività/frammento. Inoltre non si dovrebbe rendere un callback statico (potrebbe causare problemi quando si utilizza lo stesso adattatore in molte classi). Dovresti creare un campo all'interno di ViewHolder. Quindi:

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

Dopo aver effettuato l'adattatore è possibile richiamare così:

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