web-dev-qa-db-fra.com

Comment envoyer des données de DialogFragment à un fragment?

J'ai un fragment qui ouvre une Dialogfragment pour obtenir une entrée utilisateur (une chaîne et un entier). Comment puis-je renvoyer ces deux choses dans le fragment?

Voici mon DialogFragment:

public class DatePickerFragment extends DialogFragment {
    String Month;
    int Year;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        getDialog().setTitle(getString(R.string.Date_Picker));
        View v = inflater.inflate(R.layout.date_picker_dialog, container, false);

        Spinner months = (Spinner) v.findViewById(R.id.months_spinner);
        ArrayAdapter<CharSequence> monthadapter = ArrayAdapter.createFromResource(getActivity(),
                R.array.Months, R.layout.picker_row);
              months.setAdapter(monthadapter);
              months.setOnItemSelectedListener(new OnItemSelectedListener(){
                  @Override
                  public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int monthplace, long id) {
                      Month = Integer.toString(monthplace);
                  }
                  public void onNothingSelected(AdapterView<?> parent) {
                    }
              });

        Spinner years = (Spinner) v.findViewById(R.id.years_spinner);
        ArrayAdapter<CharSequence> yearadapter = ArrayAdapter.createFromResource(getActivity(),
             R.array.Years, R.layout.picker_row);
        years.setAdapter(yearadapter);
        years.setOnItemSelectedListener(new OnItemSelectedListener(){
          @Override
          public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int yearplace, long id) {
              if (yearplace == 0){
                  Year = 2012;
              }if (yearplace == 1){
                  Year = 2013;
              }if (yearplace == 2){
                  Year = 2014;
              }
          }
          public void onNothingSelected(AdapterView<?> parent) {}
        });

        Button button = (Button) v.findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
           public void onClick(View v) {
               getDialog().dismiss();
            }
        });

        return v;
    }
}

Je dois envoyer les données après avoir cliqué sur le bouton et avant getDialog().dismiss()

Voici le fragment auquel les données doivent être envoyées:

public class CalendarFragment extends Fragment {
int Year;
String Month;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    int position = getArguments().getInt("position");
    String[] categories = getResources().getStringArray(R.array.categories);
    getActivity().getActionBar().setTitle(categories[position]);
    View v = inflater.inflate(R.layout.calendar_fragment_layout, container, false);    

    final Calendar c = Calendar.getInstance();
    SimpleDateFormat month_date = new SimpleDateFormat("MMMMMMMMM");
    Month = month_date.format(c.getTime());
    Year = c.get(Calendar.YEAR);

    Button button = (Button) v.findViewById(R.id.button);
    button.setText(Month + " "+ Year);
    button.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
           new DatePickerFragment().show(getFragmentManager(), "MyProgressDialog");
        }
    });
   return v;
  }
}

ainsi, une fois que l'utilisateur sélectionne une date dans la Dialogfragment, il doit renvoyer le mois et l'année. 

Ensuite, le texte sur le bouton doit changer pour le mois et l'année spécifiés par l'utilisateur.

32
trickedoutdavid

NOTE: Outre un ou deux appels spécifiques au fragment Android, il s'agit d'une recette générique pour la mise en œuvre de l'échange de données entre composants faiblement couplés. Vous pouvez utiliser cette approche en toute sécurité pour échanger des données entre tout, que ce soit des fragments, des activités, des dialogues ou tout autre élément de votre application.


Voici la recette:

  1. Créez interface (nommée MyContract) contenant une signature de la méthode permettant de transmettre les données, c.-à-d. methodToPassData(... data);.
  2. Assurez-vous que votre DialogFragment remplit ce contrat (ce qui signifie généralement implémente l'interface souhaitée): class MyFragment extends Fragment implements MyContract {....}
  3. Lors de la création de DialogFragment, définissez votre appelant Fragment comme son fragment cible } _ en appelant myDialogFragment.setTargetFragment(this, 0);. C'est l'objet avec lequel vous allez parler plus tard.
  4. Dans votre DialogFragment, récupérez ce fragment appelant en appelant getTargetFragment(); et convertissez objet renvoyé vers l'interface de contrat créée à l'étape 1, à l'aide de: MyContract mHost = (MyContract)getTargetFragment();. Casting nous permet de nous assurer que l'objet cible implémente le contrat requis et que nous pouvons nous attendre à ce que methodToPassData() soit présent. Si ce n'est pas le cas, vous aurez alors ClassCastException régulier. Cela ne devrait normalement pas arriver, à moins que vous ne fassiez trop de codage copier-coller :) Si votre projet utilise du code externe, des bibliothèques ou des plugins, etc., dans ce cas, attrapez plutôt l'exception et indiquez à l'utilisateur que le plugin n'est pas compatible au lieu de laissez simplement l'application se bloquer.
  5. Lorsque vient le temps de renvoyer les données, appelez methodToPassData() sur l'objet que vous avez obtenu précédemment: ((MyContract)getTargetFragment()).methodToPassData(data);. Si votre onAttach() transforme et assigne déjà le fragment cible à une variable de classe (c.-à-d. mHost), alors ce code serait simplement mHost.methodToPassData(data);.
  6. Voilà.

Vous venez de transmettre avec succès vos données de la boîte de dialogue à l'appel de fragment.

82
Marcin Orlowski

Voici une autre recette sans utiliser aucune interface. En utilisant simplement les variables setTargetFragment et Bundle pour transmettre des données entre DialogFragment et Fragment.

public static final int DATEPICKER_FRAGMENT = 1; // class variable

1 . Appelez la DialogFragment comme indiqué ci-dessous:

// create dialog fragment
DatePickerFragment dialog = new DatePickerFragment();

// optionally pass arguments to the dialog fragment
Bundle args = new Bundle();
args.putString("pickerStyle", "fancy");
dialog.setArguments(args);

// setup link back to use and display
dialog.setTargetFragment(this, DATEPICKER_FRAGMENT);
dialog.show(getFragmentManager().beginTransaction(), "MyProgressDialog")

2 . Utilisez la variable supplémentaire Bundle dans une Intent dans la DialogFragment pour transmettre toute information au fragment cible. Le code ci-dessous dans l'événement Button#onClick() de DatePickerFragment transmet une chaîne et un entier.

Intent i = new Intent()
        .putExtra("month", getMonthString())
        .putExtra("year", getYearInt());
getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, i);
dismiss();

3 . Utilisez la méthode CalendarFragment's onActivityResult() pour lire les valeurs:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
        case DATEPICKER_FRAGMENT:
            if (resultCode == Activity.RESULT_OK) {
                Bundle bundle = data.getExtras();
                String mMonth = bundle.getString("month", Month);
                int mYear = bundle.getInt("year");
                Log.i("PICKER", "Got year=" + year + " and month=" + month + ", yay!");
            } else if (resultCode == Activity.RESULT_CANCELED) {
                ...
            }
            break;
    }
}
35
blizzard

Un bon conseil est d’utiliser l’approche ViewModel et LiveData qui est la meilleure solution. Voici un exemple de code sur la manière de partager des données entre fragments:

class SharedViewModel : ViewModel() {
    val selected = MutableLiveData<Item>()

    fun select(item: Item) {
        selected.value = item
    }
}

class MasterFragment : Fragment() {

    private lateinit var itemSelector: Selector

    private lateinit var model: SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        model = activity?.run {
            ViewModelProviders.of(this).get(SharedViewModel::class.Java)
        } ?: throw Exception("Invalid Activity")
        itemSelector.setOnClickListener { item ->
            // Update the UI
        }
    }
}

class DetailFragment : Fragment() {

    private lateinit var model: SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        model = activity?.run {
            ViewModelProviders.of(this).get(SharedViewModel::class.Java)
        } ?: throw Exception("Invalid Activity")
        model.selected.observe(this, Observer<Item> { item ->
            // Update the UI
        })
    }
}

Essayez, ces nouveaux composants viennent nous aider, je pense que c'est plus efficace qu'une autre approche.

En savoir plus à ce sujet: https://developer.Android.com/topic/libraries/architecture/viewmodel

0
Pedro Massango