Passando i dati tra un frammento e la sua attività di contenitore


177

Come posso passare i dati tra un frammento e la sua attività di contenitore? Esiste qualcosa di simile al passaggio di dati tra attività attraverso intenti?

Ho letto questo, ma non ha aiutato molto:
http://developer.android.com/guide/topics/fundamentals/fragments.html#CommunicatingWithActivity


È insufficiente? Puoi inviare tutto quello che vuoi, forse puoi spiegare di più, cosa vuoi realizzare?
Marcin Milejski,

Dal documento non capivo come, ad esempio, avrei passato un valore o una stringa dall'attività al frammento o viceversa. Grazie
tyb

2
Il modo migliore per gestire le chiamate in modo asincrono dai frammenti e restituirli è implementare BroadcastReceivers su entrambi i lati. Dovresti comunque renderlo asincrono se lavori con un numero non specifico di frammenti.
Marek Sebera,

@punisher_malade No, funziona per me
Vadim Kotov,

Risposte:


211

Nel tuo frammento puoi chiamare getActivity().

Questo ti darà accesso all'attività che ha creato il frammento. Da lì puoi ovviamente chiamare qualsiasi tipo di metodo di accesso presente nell'attività.

ad es. per un metodo chiamato getResult()per la tua attività:

((MyActivity) getActivity()).getResult();

86
Dato che stai accedendo a una funzione all'interno della TUA attività (e non dell'attività Android principale) dovrai lanciare la tua chiamata getActivity (): ((MyActivity) getActivity ()). GetResult ();
Nick,

3
Come sarà forse il metodo back, dal frammento all'attività?
Vasil Valchev,

6
@VasilValchev, è possibile creare un'interfaccia e forzare l'attività a implementarla, quindi chiamare un metodo dall'interno del frammento per passare i dati. Utilizzare il metodo onAttach per verificare se l'attività implementa l'interfaccia.
Ivan Nikolov,

3
Ottima risposta di @IvanNikolov. Puoi trovare una spiegazione approfondita nel link di formazione sui frammenti
bogdan

8
Questa non è una buona pratica né modello. La risposta con le interfacce è quella corretta.
Moictab,

304

Prova a usare le interfacce.

Qualsiasi frammento che dovrebbe restituire i dati alla sua attività di contenimento dovrebbe dichiarare un'interfaccia per gestire e trasmettere i dati. Quindi assicurati che le tue attività di contenimento implementino quelle interfacce. Per esempio:

GIAVA

Nel tuo frammento, dichiara l'interfaccia ...

public interface OnDataPass {
    public void onDataPass(String data);
}

Quindi, collega l'implementazione della classe contenente dell'interfaccia al frammento nel metodo onAttach, in questo modo:

OnDataPass dataPasser;

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    dataPasser = (OnDataPass) context;
}

Nel tuo frammento, quando devi gestire il passaggio di dati, chiamalo semplicemente sull'oggetto dataPasser:

public void passData(String data) {
    dataPasser.onDataPass(data);
}

Infine, nella tua attività di contenimento che implementa OnDataPass ...

@Override
public void onDataPass(String data) {
    Log.d("LOG","hello " + data);
}

Kotlin

Passaggio 1. Creare l'interfaccia

interface OnDataPass {
    fun onDataPass(data: String)
}

Passaggio 2. Quindi, collegare l'implementazione della classe di contenimento dell'interfaccia al frammento nel metodo onAttach (YourFragment), in questo modo:

lateinit var dataPasser: OnDataPass

override fun onAttach(context: Context) {
    super.onAttach(context)
    dataPasser = context as OnDataPass
}

Passaggio 3. Nel frammento, quando è necessario gestire il passaggio di dati, è sufficiente chiamarlo sull'oggetto dataPasser:

fun passData(data: String){
    dataPasser.onDataPass(data)
}

Passaggio 4. Infine, nella tua attività implementa OnDataPass

class MyActivity : AppCompatActivity(), OnDataPass {}

override fun onDataPass(data: String) {
    Log.d("LOG","hello " + data)
}

2
Grazie buona risposta, al punto. Questo è anche molto ben spiegato qui . Quello che non ho realizzato è che puoi implementare più interfacce, stavo già implementando un ActionBar.TabListenere ho dovuto aggiungere un'interfaccia aggiuntiva.
Eugene van der Merwe,

5
Questa è sicuramente la strada da percorrere e secondo me l'UNICA strada da percorrere. E fornisce interazione a due vie tra l'attività e il frammento. Inoltre consentirà un mezzo per comunicare tra i frammenti quando si hanno più frammenti in una sola attività tramite schede o layout multi-frammento.
Christopher,

1
C'è qualcosa che mi chiedo ... Questa è la risposta ufficiale, e dal sito ufficiale degli sviluppatori dicono che questo è il modo giusto di comunicare frag-act-frag, ma perché è persino possibile farlo attraverso il cast getActivity ( )?
unmultimedio,

3
Perché qualcuno ha bisogno di un'interfaccia extra per farlo? Perché consideri la seguente soluzione negativa o migliore? ((MyActivity) getActivity) .myMethod (...)
spacifici

3
onAttach (Attività attività) è obsoleto, utilizzare invece OnAttach (contesto di contesto).
Chris.C

23

Approccio più semplice ma non raccomandato

È possibile accedere ai dati delle attività dal frammento:

Attività:

public class MyActivity extends Activity {

    private String myString = "hello";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        ...
    }

    public String getMyData() {
        return myString;
    }
}

Frammento:

public class MyFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        MyActivity activity = (MyActivity) getActivity();
        String myDataFromActivity = activity.getMyData();
        return view;
    }
}

5
Ciò aggiunge un elevato accoppiamento al codice ed è una cattiva pratica. Ora non puoi usare il frammento in attività diverse daMyActivity
AmeyaB,

perché in questo modo è considerata una cattiva pratica e l'interfaccia è solo la soluzione di questa domanda?
androidXP,

AmeyaB se tutta l'attività si estende da una BaseActivity o se la lanciamo come BaseActivity activity = (BaseActivity) getActivity (); quindi funzionerà su tutte le attività
Zar E Ahmer,

21
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    Bundle b = getActivity().getIntent().getExtras();
            wid = b.getString("wid");
            rid = b.getString("rid");
            View view = inflater.inflate(R.layout.categoryfragment, container, false);
    return view;
 }

14
Ecco come ottenerlo nel frammento, ma come impostarlo nella classe chiamando il frammento se si dispone del frammento nel file di layout XML.
Zapnologica

17

Passando i dati tra un frammento e la sua attività di contenitore

Attività:

        Bundle bundle = new Bundle();
        bundle.putString("message", "Alo Elena!");
        FragmentClass fragInfo = new FragmentClass();
        fragInfo.setArguments(bundle);
        transaction.replace(R.id.fragment_single, fragInfo);
        transaction.commit();

Frammento:

Lettura del valore nel frammento

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        String myValue = this.getArguments().getString("message");
        ...
        ...
        ...
        }

ciao @Elenasys. Come posso inviare i dati del bundle indietro al frammento dall'attività. Sono passato dal frammento all'attività e devo tornare al frammento che è già stato creato (chiama solo su Start, onResume, ...) e ho bisogno dei dati che sono stato in grado di ottenere in attività. C'è un modo per farlo o sto facendo qualcosa di sbagliato?
Michal Moravik,

7

Non so se questo è il modo migliore o no Bu ho cercato su Google per un po 'di tempo trovando come posso passare un Bundle da un frammento alla sua attività contenitore ma invece ho trovato solo l'invio di dati dall'attività al frammento (che è stato un po 'confuso per me dato che sono un principiante).

più tardi ho provato qualcosa di mio che ha funzionato esattamente per me come volevo. quindi lo posterò qui nel caso qualcuno come me stesse cercando la stessa cosa.

// Passaggio di dati dal frammento.

Bundle gameData = new Bundle();
        gameData.putStringArrayList(Constant.KEY_PLAYERS_ARR,players);
        gameData.putString(Constant.KEY_TEAM_NAME,custom_team_name);
        gameData.putInt(Constant.KEY_REQUESTED_OVER,requestedOver);

        Intent intent = getActivity().getIntent();
        intent.putExtras(gameData);

// Ottenere dati dal bundle dall'attività contenitore.

Bundle gameData = getIntent().getExtras();
        if (gameData != null)
        {
            int over = gameData.getInt(Constant.KEY_REQUESTED_OVER);
            ArrayList<String> players = gameData.getStringArrayList(Constant.KEY_PLAYERS_ARR);
            String team = gameData.getString(Constant.KEY_TEAM_NAME);

        }
        else if (gameData == null)
        {
            Toast.makeText(this, "Bundle is null", Toast.LENGTH_SHORT).show();
        }

6

L'interfaccia è una delle migliori soluzioni:

Interfaccia colla:

public interface DataProviderFromActivity {

    public String getName();
    public String getId);

}  

MyActivity:

public class MyActivity implements DataProviderFromActivity{

    String name = "Makarov";
    String id = "sys533";

    ... ... ... ... ... .... .... 
    ... ... ... ... ... .... .... 

    public String getName(){
        return name;
    };
    public String getId(){
        return id;
    };
}

MyFragment:

public class MyFragment extends Fragment{

    String fragName = "";
    String fragId = "";

    ... ... ... ... ... .... .... 
    ... ... ... ... ... .... .... 

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        DataProviderFromActivity myActivity= (DataProviderFromActivity) getActivity();
        fragName = myActivity.getName();
        fragId = myActivity.getId();

        ... ... ... ... ... .... .... 
        ... ... ... ... ... .... .... 

        updateFragmentView();
    }
}

Ciao @HassanMakarov Mi piace la tua interfaccia, semplice e pulita. Ho un problema però. Non so se è specifico per il mio viewpager ma riesco a ottenere i dati in massimo 2 frammenti alla volta? Le stringhe "Makarov" e "sys533" appaiono nei frammenti 1 e 2 quando viene creata l'attività ma le stringhe non vengono visualizzate nel frammento 3 fino a quando non viene selezionato il frammento 2 o 3? Qualche idea?
JC23

@ JC23 Immagino che il problema sia legato alla transazione di frammenti. Quale frammento viene impostato all'avvio di MainActivity?
Hassan Tareq,

@ JC23 Cosa faccio: invece di Tab / ViewPager utilizzo Toolbar e NavigationView. In onNavigationItemSelected () eseguo transazioni di frammenti per impostare i frammenti di conseguenza.
Hassan Tareq,

2

Ho usato un AppCompatActivity che implementa gli ascoltatori di date. I frammenti sono diventati una necessità poiché avevo bisogno di codificare un selettore di intervalli di date. E avevo anche bisogno che il contenitore ricevesse le date selezionate per restituirle all'attività principale.

Per l'attività del contenitore, questa è la dichiarazione di classe:

public class AppCompatDateRange extends AppCompatActivity implements
    DateIniRangeFragment.OnDateIniSelectedListener, DateFimRangeFragment.OnDateFimSelectedListener

E le interfacce per i callback:

@Override
public void onDateIniSelected(String dataIni) {
    Log.i("data inicial:", dataIni);
}

@Override
public void onDateFimSelected(String dataFim) {
    Log.i("data final:", dataFim);
}

I callback sono stringhe perché le date sono parametri in una selezione di query.

Il codice per i frammenti (basato sul frammento di data iniziale):

public class DateIniRangeFragment extends Fragment {
OnDateIniSelectedListener callbackIni;

private DatePicker startDatePicker;

public DateIniRangeFragment() {
    ///required empty constructor
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

///through this interface the fragment sends data to the container activity
public interface OnDateIniSelectedListener {
    void onDateIniSelected(String dataIni);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ///layout for the fragment
    View v = inflater.inflate(R.layout.date_ini_fragment, container, false);

    ///initial date for the picker, in this case, current date
    startDatePicker = (DatePicker) v.findViewById(R.id.start_date_picker_appcompat);
    Calendar c = Calendar.getInstance();
    int ano = c.get(Calendar.YEAR);
    int mes = c.get(Calendar.MONTH);
    int dia = c.get(Calendar.DAY_OF_MONTH);
    startDatePicker.setSpinnersShown(false);
    startDatePicker.init(ano, mes, dia, dateSetListener);

    return v;
}

///listener that receives the selected date
private DatePicker.OnDateChangedListener dateSetListener = new DatePicker.OnDateChangedListener() {
    public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
        if (view.isShown()) { ///if the datepicker is on the screen
            String sDataIni = year + "-" + (monthOfYear + 1) + "-" + dayOfMonth;
            callbackIni.onDateIniSelected(sDataIni); //apply date to callback, string format
        }
    }
};

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    /*
    * this function guarantees that the container activity implemented the callback interface
    * */
    try {
        callbackIni = (OnDateIniSelectedListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " deve implementar OnDateIniSelectedListener");
    }
}

}

Per comporre il contenitore + i frammenti, ho usato un ViewPager (AppCompat) con una classe personalizzata che estende FragmentPagerAdapter. Nessuna finestra di dialogo.


2

Semplicemente puoi usare EventBus , è facile e funziona alla grande

EventBus in 3 passaggi

  1. Definisci eventi:

    public static class MessageEvent { /* Additional fields if needed */ }

  2. Preparare abbonati: dichiarare e annotare il metodo di abbonamento, facoltativamente specificare una modalità thread:

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MessageEvent event) {/* Do something */};

Registra e annulla la registrazione del tuo abbonato. Ad esempio su Android, le attività e i frammenti dovrebbero generalmente registrarsi in base al loro ciclo di vita:

 @Override
 public void onStart() {
     super.onStart();
     EventBus.getDefault().register(this);
 }

 @Override
 public void onStop() {
     super.onStop();
     EventBus.getDefault().unregister(this);
 }
  1. Pubblica eventi:

    EventBus.getDefault().post(new MessageEvent());


1

So che potrebbe essere in ritardo. Ma mi sono sempre perso anche su questa domanda. Sto condividendo questo link ... perché questa è forse la migliore spiegazione che io abbia mai trovato sul web per questo. Questo risolve Fragment to Activity e Fragment to Fragment !

Risolto e spiegato molto bene


0

Questo funziona per me ..

in Attività aggiungi questo metodo

    public void GetData(String data)
     {
        // do something with your data        
     }

e in Frammento aggiungi questa riga

((YourActivity)getContext).GetData("your data here");

0
public class Fragmentdemo extends Fragment {

  public interface onDemoEventListener {
    public void demoEvent(String s);
  }

  onDemoEventListener demoEventListener;

  @Override
  public void onAttach(Activity activity) {
    super.onAttach(activity);
        try {
          demoEventListener = (onDemoEventListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement onDemoEventListener");
        }
  }

  final String LOG_TAG = "TAG";

  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.fragmentdemo, null);

    Button button = (Button) v.findViewById(R.id.button);
    button.setOnClickListener(new OnClickListener() {
      public void onClick(View v) {
        demoEventListener.someEvent("Test text to Fragment1");
      }
    });
    enter code here
    return v;
  }
}

-12

Un altro modo semplice per ottenere dati, passati da un'altra attività, in un frammento in un'attività contenitore: ad esempio:

Activity_A => Activity_B (frammento)

Nella tua Activity_A crei un intento come se stessi inviando un dato (String qui) a un'altra attività:

Intent intent = new Intent(getBaseContext(),Activity_B.class);
intent.putExtra("NAME", "Value");
startActivity(intent);

nel tuo frammento, contenuto nel tuo Activity_B:

String data = getActivity().getIntent().getExtras();

getBaseContext()mi dà il seguente errore:The method getBaseContext() is undefined for the type new View.OnClickListener(){}
Si8
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.