Ошибка возврата даты из фрагмента DatePickerDialog при использовании контроллера навигации - PullRequest
3 голосов
/ 27 мая 2019

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

Я использую конфонент Навигация для отображения диалогового окна. Может ли это быть причиной проблемы?

Вот мой DatePickerDialog:

class DatePickerFragment : DialogFragment(), DatePickerDialog.OnDateSetListener {

    private var onDateSetListener: DatePickerDialog.OnDateSetListener? = null

    fun setListeningActivity(listener: DatePickerDialog.OnDateSetListener) {
        onDateSetListener = listener
    }

    override fun onDateSet(view: DatePicker?, year: Int, month: Int, dayOfMonth: Int) {
        onDateSetListener?.onDateSet(view,year, month, dayOfMonth)
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        // Use the current date as the default date in the picker
        val c = Calendar.getInstance()
        val year = c.get(Calendar.YEAR)
        val month = c.get(Calendar.MONTH)
        val day = c.get(Calendar.DAY_OF_MONTH)

        // Create a new instance of DatePickerDialog and return it
        return DatePickerDialog(context, this, year, month, day)
    }
}

Соответствующий код из фрагмента, вызывающего Datepicker:

private fun showDatePicker(hasFocus: Boolean, view: View) {
    Log.i(FRAGMENT_NAME, "Has focus $hasFocus")
    val fragment = DatePickerFragment()
    fragment.setListeningActivity(this)
    if (hasFocus) {
        Navigation.findNavController(view).navigate(R.id.datePickerFragment)
    }
}

override fun onDateSet(view: DatePicker, year: Int, month: Int, day: Int) {
    Log.i(FRAGMENT_NAME, "Date received: $year-$month-$day")
    fragmentView.dateOfBirthField.editText?.setText("$year-$month-$day")
}

Вот как я вызываю метод showDatePicker:

gmentView.dateOfBirthField.editText? .SetOnFocusChangeListener {_, hasFocus -> showDatePicker (hasFocus, фрагментView)}

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

Ответы [ 2 ]

1 голос
/ 03 июня 2019

Наилучшим способом передачи данных фрагмента является livedata с viewmodel, когда вы используете компонент архитектуры навигации.

Шаг-1 Создайте ViewModel, который будет содержать ваш selectedDate. Создайте экземпляр LiveData, чтобы вы могли наблюдать за изменениями.

class SharedViewModel: ViewModel() {
    val selectedDate: MutableLiveData<String> = MutableLiveData()
}

Step-2 Получите экземпляр SharedViewModel и обновите переменную selectedDate, чтобы уведомить своих наблюдателей. (In DatePickerFragment)

override fun onDateSet(view: DatePicker, year: Int, month: Int, day: Int) {
    val viewModel = ViewModelProviders.of(activity!!)[SharedViewModel::class.java]
    viewModel.selectedDate.value = "$year $month $day"
}

Шаг-3 Получите экземпляр SharedViewModel, где вы хотите наблюдать изменения. (В нашем случае откуда взято datePickerFragment.)

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)

    ViewModelProviders.of(activity!!)[SharedViewModel::class.java]
            .selectedDate
            .observe(activity!!, Observer { dateOfBirthField.editText.setText(it) })
}

Если вы используете androidx артефакт. Тогда вам может понадобиться следующая зависимость.

implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0-alpha01'
0 голосов
/ 30 мая 2019

Можно установить цель Fragment:

val fragment = DatePickerFragment()
fragment.setTargetFragment(this, requestCode)

или пусть он доставит результат в Activity:

val fragment = DatePickerFragment()
fragment.setListeningActivity(this)

это должно произойти до показа DialogFragment; так что DialogFragment уже будет знать, куда он должен доставить свой результат. Для любого места назначения метод onDateSet() должен вернуть Intent с дополнительными значениями Bundle ... и onActivityResult () должен обработать этот результат и соответствующим образом обновить графический интерфейс. Так работает связь между Activity & Fragment - и это также относится к DialogFragment. Метод onActivityResult() может обрабатывать обратные вызовы от Activity и Fragment.

В Java это было бы что-то похожее - для возврата результата в DialogFragment в методе override fun onDateSet(view: DatePicker, year: Int, month: Int, day: Int):

/* converting the selection to long integer */
GregorianCalendar date = new java.util.GregorianCalendar();
date.set(GregorianCalendar.YEAR, year);
date.set(GregorianCalendar.MONTH, month);
date.set(GregorianCalendar.DAY_OF_MONTH, day);
Log.d(LOG_TAG, "confirmed " + date.getTime());
long dueDate = date.getTime().getTime();

if (getTargetFragment() != null) {

    /* stuffing the result value into a Bundle */
    Intent intent = this.getActivity().getIntent();
    Bundle extras = new Bundle();
    extras.putLong(ArgumentKeys.ARGUMENT_SELECT_DUE_DATE, dueDate);
    intent.putExtras(extras);

    /* this delivers the result */
    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, intent);

    /* close dialog */
    this.dismiss();

} else {
    Log.w(LOG_TAG, ".setTargetFragment() had not been called.");
}

или аналогично для возврата на Activity:

if(this.dialog.getOwnerActivity() != null) {
    ...
}

и onActivityResult() Activity или Fragment, открывших DialogFragment:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
    Bundle extras = intent.getExtras();
    if(resultCode == Activity.RESULT_OK) {
        switch (requestCode) {
            case RequestCodes.REQUESTCODE_TASK_DUE_DATE: {
                if(extras != null) {
                    long dueDate = extras.getLong(ArgumentKeys.ARGUMENT_SELECT_DUE_DATE, -1);

                        /* set the value in here */
                    }
                }
            }
            ...
    }
}

возврат результата Fragment работает почти так же, как и результат Activity.

...