Подтвердить что ты не робот

Как отправить данные из диалогового окна в фрагмент?

У меня есть фрагмент, который открывает Dialogfragment, чтобы получить пользовательский ввод (строка и целое число). Как отправить эти две вещи обратно в фрагмент?

Вот мой 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;
    }
}

Мне нужно отправить данные после нажатия кнопки и до getDialog().dismiss()

Вот фрагмент, который необходимо отправить данным:

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

поэтому, как только пользователь выбирает дату в Dialogfragment, он должен вернуть месяц и год.

Затем текст на кнопке должен измениться на месяц и год, указанные пользователем.

4b9b3361

Ответ 1

ПРИМЕЧАНИЕ. Помимо одного или двух вызовов, специфичных для фрагмента Android, это общий подход к реализации обмена данными между слабосвязанными компонентами. Вы можете безопасно использовать этот подход для обмена данными буквально с чем угодно, будь то фрагменты, действия, диалоги или любые другие элементы вашего приложения.


Вот рецепт:

  1. Создайте interface (то есть с именем MyContract), содержащий сигнатуру метода для передачи данных, то есть methodToPassData(... data);.
  2. Убедитесь, что ваш DialogFragment полностью заполнен этим контрактом (что обычно означает реализацию желаемого интерфейса): class MyFragment extends Fragment implements MyContract {....}
  3. При создании DialogFragment установите ваш вызывающий Fragment в качестве целевого фрагмента, вызвав myDialogFragment.setTargetFragment(this, 0);. Это объект, с которым вы будете разговаривать позже.
  4. В вашем DialogFragment получите этот вызывающий фрагмент, вызвав getTargetFragment();, и приведите возвращенный объект к интерфейсу контракта, созданному вами на шаге 1, выполнив: MyContract mHost = (MyContract)getTargetFragment();. Приведение позволяет нам гарантировать, что целевой объект реализует необходимый контракт, и мы можем ожидать, что methodToPassData() будет там. Если нет, то вы получите обычный ClassCastException. Это обычно не должно происходить, если вы не выполняете слишком много операций копирования-вставки :) Если ваш проект использует внешний код, библиотеки или плагины и т.д., И в таком случае вам лучше отловить исключение и сказать пользователю, т.е. плагин не совместим вместо просто дайте приложению упасть.
  5. Когда придет время отправлять данные обратно, вызовите methodToPassData() для объекта, который вы получили ранее: ((MyContract)getTargetFragment()).methodToPassData(data);. Если ваш onAttach() уже преобразует и назначает целевой фрагмент переменной класса (т.е. mHost), тогда этот код будет просто mHost.methodToPassData(data);.
  6. Вуаля.

Вы только что успешно передали свои данные из диалога обратно к вызывающему фрагменту.

Ответ 2

Здесь другой рецепт без использования интерфейса. Просто используя setTargetFragment и Bundle для передачи данных между DialogFragment и Fragment.

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

1. Вызовите DialogFragment, как показано ниже:

// 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. Используйте дополнительный Bundle в Intent в DialogFragment, чтобы передать информацию обратно целевому фрагменту. Следующий код в Button#onClick() событии DatePickerFragment передает строку и целое число.

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

3. Используйте метод CalendarFragment onActivityResult() для чтения значений:

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

Ответ 3

Хороший совет - использовать подход ViewModel и LiveData, который является наилучшим способом. Ниже приведен пример кода о том, как обмениваться данными между фрагментами:

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

Попробуйте эти новые компоненты, чтобы помочь нам, я думаю, что это более эффективно, чем другой подход.

Узнайте больше об этом: https://developer.android.com/topic/libraries/architecture/viewmodel

Ответ 4

Здесь подход, иллюстрирующий ответ марцина, реализован в kotlin.

1. Создайте интерфейс, в котором есть метод для передачи данных в вашем классе dialogFragment.

interface OnCurrencySelected{
    fun selectedCurrency(currency: Currency)
}

2. Добавьте свой интерфейс в конструктор dialogFragment.

class CurrencyDialogFragment(val onCurrencySelected :OnCurrencySelected)    :DialogFragment() {}

3. Теперь заставьте ваш фрагмент реализовать интерфейс, который вы только что создали

class MyFragment : Fragment(), CurrencyDialogFragment.OnCurrencySelected {

override fun selectedCurrency(currency: Currency) {
//this method is called when you pass data back to the fragment
}}

4. Затем, чтобы показать ваш диалог CurrencyDialogFragment(this).show(fragmentManager,"dialog"). this - это интерфейсный объект, с которым вы будете разговаривать, для передачи данных обратно в ваш фрагмент.

5. Когда вы хотите отправить данные обратно в свой фрагмент, просто вызовите метод для передачи данных в объект интерфейса, который вы передали в конструктор dialogFragment.

onCurrencySelected.selectedCurrency(Currency.USD)
dialog.dismiss()