使用导航控制器时无法从 DatePickerDialog 片段返回日期
Trouble returning date from DatePickerDialog fragment when using Navigation Controller
我正在尝试 return 从我的 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)
}
}
调用日期选择器的片段中的相关代码:
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 方法的方式:
fragmentView.dateOfBirthField.editText?.setOnFocusChangeListener { _, hasFocus -> showDatePicker(hasFocus, fragmentView) }
问题:如何将日期返回到调用 DatePickerDialog 的片段中。我不想将 DatePickerDialog 作为调用它的 class 的内部 class。
可以设置一个目标 Fragment
:
val fragment = DatePickerFragment()
fragment.setTargetFragment(this, requestCode)
或者让它把结果传递给 Activity
:
val fragment = DatePickerFragment()
fragment.setListeningActivity(this)
这应该在显示 DialogFragment
之前发生;这样 DialogFragment
就会知道它必须将结果传递到哪里。对于任何一个交付目的地,方法 onDateSet()
需要 return 一个带有额外 Bundle
的 Intent
... 并且 onActivityResult() 需要处理该结果并更新 GUI因此。这就是 Activity
和 Fragment
之间的通信方式——这也适用于 DialogFragment
。方法 onActivityResult()
可以处理来自 Activity
和 Fragment
.
的回调
在 Java 中类似于这样 - 对于 return 在 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.");
}
或类似地 returning 到 Activity
:
if(this.dialog.getOwnerActivity() != null) {
...
}
和Activity
或Fragment
的onActivityResult()
打开了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 */
}
}
}
...
}
}
return Fragment
结果与 return Activity
结果几乎相同。
片段数据传递的最佳方式是 livedata
和 viewmodel
,当您使用导航架构组件时。
第 1 步 创建 ViewModel
来容纳你的 selectedDate
。创建 LiveData
的实例,以便您可以观察更改。
class SharedViewModel: ViewModel() {
val selectedDate: MutableLiveData<String> = MutableLiveData()
}
Step-2 获取 SharedViewModel
的实例并更新 selectedDate
变量以通知它的观察者。 (在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"
}
Step-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'
我正在尝试 return 从我的 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)
}
}
调用日期选择器的片段中的相关代码:
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 方法的方式:
fragmentView.dateOfBirthField.editText?.setOnFocusChangeListener { _, hasFocus -> showDatePicker(hasFocus, fragmentView) }
问题:如何将日期返回到调用 DatePickerDialog 的片段中。我不想将 DatePickerDialog 作为调用它的 class 的内部 class。
可以设置一个目标 Fragment
:
val fragment = DatePickerFragment()
fragment.setTargetFragment(this, requestCode)
或者让它把结果传递给 Activity
:
val fragment = DatePickerFragment()
fragment.setListeningActivity(this)
这应该在显示 DialogFragment
之前发生;这样 DialogFragment
就会知道它必须将结果传递到哪里。对于任何一个交付目的地,方法 onDateSet()
需要 return 一个带有额外 Bundle
的 Intent
... 并且 onActivityResult() 需要处理该结果并更新 GUI因此。这就是 Activity
和 Fragment
之间的通信方式——这也适用于 DialogFragment
。方法 onActivityResult()
可以处理来自 Activity
和 Fragment
.
在 Java 中类似于这样 - 对于 return 在 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.");
}
或类似地 returning 到 Activity
:
if(this.dialog.getOwnerActivity() != null) {
...
}
和Activity
或Fragment
的onActivityResult()
打开了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 */
}
}
}
...
}
}
return Fragment
结果与 return Activity
结果几乎相同。
片段数据传递的最佳方式是 livedata
和 viewmodel
,当您使用导航架构组件时。
第 1 步 创建 ViewModel
来容纳你的 selectedDate
。创建 LiveData
的实例,以便您可以观察更改。
class SharedViewModel: ViewModel() {
val selectedDate: MutableLiveData<String> = MutableLiveData()
}
Step-2 获取 SharedViewModel
的实例并更新 selectedDate
变量以通知它的观察者。 (在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"
}
Step-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'