Passer des données entre un fragment et son activité conteneur


177

Comment puis-je passer des données entre un fragment et son activité de conteneur? Existe-t-il quelque chose de similaire au passage de données entre les activités via des intentions?

J'ai lu ceci, mais cela n'a pas beaucoup aidé:
http://developer.android.com/guide/topics/fundamentals/fragments.html#CommunicatingWithActivity


Est-ce que c'est insuffisant? Vous pouvez envoyer tout ce que vous voulez, peut-être pouvez-vous expliquer plus, que voulez-vous accomplir?
Marcin Milejski

À partir du document, je ne comprenais pas comment, par exemple, je passerais une valeur ou une chaîne de l'activité au fragment ou vice-versa. Merci
tyb

2
La meilleure façon de gérer les appels asynchrones à partir de fragments et de leur renvoyer des données consiste à implémenter BroadcastReceivers des deux côtés. Vous devriez de toute façon le rendre asynchrone si vous travaillez avec un nombre non spécifique de fragments.
Marek Sebera

@punisher_malade Non, ça marche pour moi
Vadim Kotov

Réponses:


211

Dans votre fragment, vous pouvez appeler getActivity().

Cela vous donnera accès à l'activité qui a créé le fragment. À partir de là, vous pouvez évidemment appeler toute sorte de méthodes d'accesseur qui se trouvent dans l'activité.

par exemple pour une méthode appelée getResult()sur votre activité:

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

86
Puisque vous accédez à une fonction dans VOTRE activité (et non à l'activité Android parente), vous devrez lancer votre appel getActivity (): ((MyActivity) getActivity ()). GetResult ();
Nick

3
Comment sera éventuellement la méthode de retour, du fragment à l'activité?
Vasil Valchev

6
@VasilValchev, vous pouvez créer une interface et forcer l'activité à l'implémenter, puis appeler une méthode à partir de votre fragment pour transmettre les données. Utilisez la méthode onAttach pour vérifier si l'activité implémente l'interface.
Ivan Nikolov

3
Excellente réponse de @IvanNikolov. Vous pouvez trouver une explication approfondie sur le lien de formation
Bogdan

8
Ce n'est ni une bonne pratique ni un modèle. La réponse avec les interfaces est la bonne.
moictab

304

Essayez d'utiliser des interfaces.

Tout fragment qui doit renvoyer des données à son activité contenant doit déclarer une interface pour gérer et transmettre les données. Assurez-vous ensuite que votre activité contenant implémente ces interfaces. Par exemple:

JAVA

Dans votre fragment, déclarez l'interface ...

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

Ensuite, connectez l'implémentation de la classe conteneur de l'interface au fragment dans la méthode onAttach, comme ceci:

OnDataPass dataPasser;

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

Dans votre fragment, lorsque vous devez gérer le passage de données, appelez-le simplement sur l'objet dataPasser:

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

Enfin, dans votre activité contenant qui implémente OnDataPass ...

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

KOTLIN

Étape 1. Créer une interface

interface OnDataPass {
    fun onDataPass(data: String)
}

Étape 2. Ensuite, connectez l'implémentation de l'interface de la classe conteneur au fragment dans la méthode onAttach (YourFragment), comme ceci:

lateinit var dataPasser: OnDataPass

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

Étape 3. Dans votre fragment, lorsque vous devez gérer le passage de données, appelez-le simplement sur l'objet dataPasser:

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

Étape 4. Enfin, dans votre activité implémente OnDataPass

class MyActivity : AppCompatActivity(), OnDataPass {}

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

2
Merci bonne réponse, au point. Ceci est également très bien expliqué ici . Ce que je n'avais pas réalisé, c'est que vous pouvez implémenter plusieurs interfaces, j'implémentais déjà un ActionBar.TabListeneret j'ai dû ajouter une interface supplémentaire.
Eugene van der Merwe

5
C'est définitivement la voie à suivre et à mon avis la SEULE voie à suivre. Et fournit une interaction bidirectionnelle entre l'activité et le fragment. Permettra également un moyen de communiquer entre les fragments lorsque vous avez plusieurs fragments dans une activité, que ce soit via des onglets ou une mise en page multi-frag.
Christopher

1
Il y a quelque chose que je me demande ... C'est la réponse officielle, et du site officiel des développeurs, ils disent que c'est la bonne façon de communiquer frag-act-frag, mais pourquoi est-il même possible de le faire via le casting getActivity ( )?
unmultimedio

3
Pourquoi quelqu'un a-t-il besoin d'une interface supplémentaire pour faire cela? Pourquoi considérez-vous la solution suivante comme mauvaise ou votre solution meilleure? ((MyActivity) getActivity) .myMethod (...)
spacifici

3
onAttach (activité d'activité) est obsolète, veuillez utiliser onAttach (contexte de contexte) à la place.
Chris.C

23

Approche la plus simple mais non recommandée

Vous pouvez accéder aux données d'activité à partir du fragment:

Activité:

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

Fragment:

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
Cela ajoute un couplage élevé à votre code et est une mauvaise pratique. Maintenant, vous ne pouvez pas utiliser le fragment dans une activité autre queMyActivity
AmeyaB

pourquoi cette façon est-elle considérée comme une mauvaise pratique et l'interface est-elle la seule solution à cette question?
androidXP

AmeyaB si toute activité s'étend à partir d'une BaseActivity ou et que nous la convertissons comme BaseActivity activity = (BaseActivity) getActivity (); puis cela fonctionnera sur toutes les activités
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
Voici comment l'obtenir dans le fragment, mais comment le définir dans la classe appelant le fragment si vous avez votre fragment dans un fichier de mise en page xml.
Zapnologica

17

Passer des données entre un fragment et son activité conteneur

Activité:

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

Fragment:

Lire la valeur dans le fragment

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

bonjour @Elenasys. Comment puis-je renvoyer des données groupées à un fragment d'activité. Je suis passé de fragment en activité et j'ai besoin de revenir au fragment qui est déjà créé (appelle uniquement onStart, onResume, ...) et j'ai besoin des données que j'ai pu obtenir en activité. Y a-t-il un moyen de le faire ou est-ce que je fais quelque chose de mal?
Michal Moravik

7

Je ne sais pas si c'est le meilleur moyen ou non Bu Je cherche sur google depuis un certain temps pour trouver comment puis-je passer un Bundle d'un fragment à son activité de conteneur, mais tout ce que j'ai trouvé était d'envoyer des données d'activité à fragment à la place (ce qui était un peu déroutant pour moi car je suis un débutant).

plus tard, j'ai essayé quelque chose qui m'est propre qui a fonctionné exactement pour moi comme je le voulais. donc je le posterai ici si quelqu'un comme moi cherche la même chose.

// Passer des données depuis Fragment.

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

// Récupération des données du bundle à partir de son activité de conteneur.

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'interface est l'une des meilleures solutions:

Interface de colle:

public interface DataProviderFromActivity {

    public String getName();
    public String getId);

}  

Mon activité:

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

Salut @HassanMakarov J'aime votre interface, simple et propre. J'ai cependant un problème. Je ne sais pas si c'est spécifique à mon viewpager mais je ne reçois les données que dans au plus 2 fragments à la fois? Les chaînes "Makarov" et "sys533" apparaissent dans les fragments 1 et 2 lorsque l'activité est créée mais les chaînes n'apparaissent pas dans le fragment 3 tant que le fragment 2 ou 3 n'est pas sélectionné? Des idées?
JC23

@ JC23 Je suppose que le problème est lié à la transaction fragmentée. Quel fragment est défini lorsque MainActivity est lancé?
Hassan Tareq

@ JC23 Ce que je fais: au lieu de Tab / ViewPager j'utilise Toolbar & NavigationView. Dans onNavigationItemSelected (), j'effectue des transactions de fragment pour définir les fragments en conséquence.
Hassan Tareq

2

J'ai utilisé un AppCompatActivity qui implémente des écouteurs de date. Les fragments sont devenus une nécessité car j'avais besoin de coder un sélecteur de plage de dates. Et j'avais également besoin du conteneur pour recevoir les dates sélectionnées pour les renvoyer à l'activité parent.

Pour l'activité conteneur, voici la déclaration de classe:

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

Et les interfaces pour les callbacks:

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

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

Les rappels sont des chaînes car les dates sont des paramètres dans une sélection de requête.

Le code des fragments (basé sur le fragment de date initial):

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

}

Pour composer le conteneur + fragments, j'ai utilisé un ViewPager (AppCompat) avec une classe personnalisée qui étend FragmentPagerAdapter. Pas de dialogues.


2

Simplement, vous pouvez utiliser EventBus, c'est facile et fonctionne très bien

EventBus en 3 étapes

  1. Définissez les événements:

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

  2. Préparez les abonnés: déclarez et annotez votre méthode d'abonnement, spécifiez éventuellement un mode de thread:

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

Enregistrez et désinscrivez votre abonné. Par exemple sur Android, les activités et les fragments doivent généralement s'enregistrer en fonction de leur cycle de vie:

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

 @Override
 public void onStop() {
     super.onStop();
     EventBus.getDefault().unregister(this);
 }
  1. Post événements:

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


1

Je sais que c'est peut-être tard. Mais j'étais aussi toujours perdu sur cette question. Je partage ce lien ... car c'est peut-être la meilleure explication que j'aie jamais trouvée sur le Web pour cela. Cela résout le fragment en activité et le fragment en fragment !

Très bien résolu et expliqué


0

Cela fonctionne pour moi..

dans Activity, ajoutez cette méthode

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

et dans Fragment ajoutez cette ligne

((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 autre moyen simple d'obtenir des données, transmises à partir d'une autre activité, dans un fragment d'une activité de conteneur: par exemple:

Activity_A => Activity_B (Fragment)

Dans votre Activity_A, vous créez un intent comme si vous envoyez une donnée (String ici) à une autre activité:

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

dans votre fragment, contenu dans votre Activity_B:

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

getBaseContext()me donne l'erreur suivante:The method getBaseContext() is undefined for the type new View.OnClickListener(){}
Si8
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.