FragmentDialog 与 TimePicker 的错误行为
Wrong behaviour of FragmentDialog with TimePicker
我正在创建一个带有自定义视图的对话框。
视图中的组件之一是 TimePicker。
这样做时,TimePicker 的 OK+Cancel 按钮将获得控制权,而 FragmentDialog 默认按钮将被禁用。
这会导致问题,因为根据用户数据我 show/hide TimePicker 视图,当它被隐藏时它也会隐藏 OK+Cancel 按钮。
拥有自定义视图很重要,这样用户就可以select在单个视图中查看所有相关数据。
编辑 - 添加显示问题的图像
layout.xml
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="1000dp"
android:paddingLeft="5dp"
android:paddingRight="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="50"
android:text="@string/automatic_backup_type"
android:textColor="@color/dialog_text_color"/>
<!-- depending on the selected value here the timePart layout is showed or hide -->
<Spinner
android:id="@+id/automaticBackupType"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="50"
android:background="@drawable/dialog_spinner_background"
android:prompt="@string/automatic_backup_type"
android:textColor="@color/dialog_text_color"
android:spinnerMode="dropdown"/>
</LinearLayout>
<!-- Some more controls here that are not relevant -->
<LinearLayout
android:id="@+id/timePart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/automatic_backup_time"
android:textColor="@color/dialog_text_color"/>
<TimePicker
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
</ScrollView>
为了显示对话框,我使用代码:
new AutomaticBackupConfigurationAlert().show(getSupportFragmentManager(), "TAG");
AutomaticBackupConfigurationAlert 代码:
public static class AutomaticBackupConfigurationAlert extends DialogFragment {
private Spinner automaticBackupType;
private View timePart;
private TimePicker time;
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AutomaticBackupType type;
Calendar time;
if (savedInstanceState != null) {
type = (AutomaticBackupType)savedInstanceState.getSerializable(KEY_TYPE);
time = (Calendar)savedInstanceState.getSerializable(KEY_TIME);
} else {
type = SettingsHelper.getAutomaticBackupType();
time = SettingsHelper.getAutomaticBackupTime();
}
LayoutInflater inflater = LayoutInflater.from(getActivity());
@SuppressLint("InflateParams")
View view = inflater.inflate(R.layout.dialog_automatic_backup, null);
this.automaticBackupType = view.findViewById(R.id.automaticBackupType);
this.timePart = view.findViewById(R.id.timePart); // When this is hide, no buttons for OK+Cancel
this.time = view.findViewById(R.id.time);
updateVisibility(type);
{
// backup type
ArrayAdapter<AutomaticBackupType> adapter = new ArrayAdapter<>(getActivity(), R.layout.dialog_spinner_item, AutomaticBackupType.values());
adapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
automaticBackupType.setAdapter(adapter);
automaticBackupType.setSelection(type.ordinal());
}
{
// time
this.time.setIs24HourView(DateFormat.is24HourFormat(getActivity()));
this.time.setCurrentHour(time.get(Calendar.HOUR_OF_DAY));
this.time.setCurrentMinute(time.get(Calendar.MINUTE));
}
automaticBackupType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
AutomaticBackupType tmpAutomaticBackupType = (AutomaticBackupType)automaticBackupType.getSelectedItem();
updateVisibility(tmpAutomaticBackupType);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.automatic_backup_configuration_title)
.setView(view)
.setPositiveButton(R.string.btn_save, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// Do nothing here because we override this button later to change the close behaviour.
// However, we still need this because on older versions of Android unless we
// pass a handler the button doesn't get instantiated
}
})
.setNegativeButton(R.string.btn_cancel, null)
.create();
}
@Override
public void onStart() {
super.onStart();
AlertDialog d = (AlertDialog)getDialog();
if (d != null) {
Button positiveButton = d.getButton(Dialog.BUTTON_POSITIVE);
positiveButton.setOnClickListener(v -> {
// save the data
});
}
}
private void updateVisibility(AutomaticBackupType tmpAutomaticBackupType) {
if (tmpAutomaticBackupType != null) {
UiHelper.setVisibility(timePart, !tmpAutomaticBackupType.equals(AutomaticBackupType.disabled), true);
}
}
}
刚刚找到解决方案(我不喜欢它,但它有效)。
我把它贴在这里以防其他人需要它。
TimePicker 似乎有 Positive/Natural/Negative 个与 AlertDialog 具有相同 ID 的按钮。当我使用对话框生成器设置它们时,它实际上在 TimePicker 中设置了一个。
一般情况下,解决方法是在Dialog中添加一个没有TimePicker视图的视图,设置好按钮后才添加。
上面的代码在两个位置发生了变化:
首先在onCreateDialog方法中时间设置块:
{
// time
this.time.setIs24HourView(DateFormat.is24HourFormat(getActivity()));
this.time.setCurrentHour(time.get(Calendar.HOUR_OF_DAY));
this.time.setCurrentMinute(time.get(Calendar.MINUTE));
timePart.removeView(this.time); // remove the TimePicker view from the layout
}
onStart 方法中的第二个
public void onStart() {
super.onStart();
AlertDialog d = (AlertDialog)getDialog();
if (d != null) {
Button positiveButton = d.getButton(Dialog.BUTTON_POSITIVE);
timePart.addView(time); // Now we can add the view
positiveButton.setOnClickListener(v -> {
// save the data
});
}
}
我正在创建一个带有自定义视图的对话框。 视图中的组件之一是 TimePicker。 这样做时,TimePicker 的 OK+Cancel 按钮将获得控制权,而 FragmentDialog 默认按钮将被禁用。
这会导致问题,因为根据用户数据我 show/hide TimePicker 视图,当它被隐藏时它也会隐藏 OK+Cancel 按钮。
拥有自定义视图很重要,这样用户就可以select在单个视图中查看所有相关数据。
编辑 - 添加显示问题的图像
layout.xml
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="1000dp"
android:paddingLeft="5dp"
android:paddingRight="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="50"
android:text="@string/automatic_backup_type"
android:textColor="@color/dialog_text_color"/>
<!-- depending on the selected value here the timePart layout is showed or hide -->
<Spinner
android:id="@+id/automaticBackupType"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="50"
android:background="@drawable/dialog_spinner_background"
android:prompt="@string/automatic_backup_type"
android:textColor="@color/dialog_text_color"
android:spinnerMode="dropdown"/>
</LinearLayout>
<!-- Some more controls here that are not relevant -->
<LinearLayout
android:id="@+id/timePart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/automatic_backup_time"
android:textColor="@color/dialog_text_color"/>
<TimePicker
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
</ScrollView>
为了显示对话框,我使用代码:
new AutomaticBackupConfigurationAlert().show(getSupportFragmentManager(), "TAG");
AutomaticBackupConfigurationAlert 代码:
public static class AutomaticBackupConfigurationAlert extends DialogFragment {
private Spinner automaticBackupType;
private View timePart;
private TimePicker time;
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AutomaticBackupType type;
Calendar time;
if (savedInstanceState != null) {
type = (AutomaticBackupType)savedInstanceState.getSerializable(KEY_TYPE);
time = (Calendar)savedInstanceState.getSerializable(KEY_TIME);
} else {
type = SettingsHelper.getAutomaticBackupType();
time = SettingsHelper.getAutomaticBackupTime();
}
LayoutInflater inflater = LayoutInflater.from(getActivity());
@SuppressLint("InflateParams")
View view = inflater.inflate(R.layout.dialog_automatic_backup, null);
this.automaticBackupType = view.findViewById(R.id.automaticBackupType);
this.timePart = view.findViewById(R.id.timePart); // When this is hide, no buttons for OK+Cancel
this.time = view.findViewById(R.id.time);
updateVisibility(type);
{
// backup type
ArrayAdapter<AutomaticBackupType> adapter = new ArrayAdapter<>(getActivity(), R.layout.dialog_spinner_item, AutomaticBackupType.values());
adapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
automaticBackupType.setAdapter(adapter);
automaticBackupType.setSelection(type.ordinal());
}
{
// time
this.time.setIs24HourView(DateFormat.is24HourFormat(getActivity()));
this.time.setCurrentHour(time.get(Calendar.HOUR_OF_DAY));
this.time.setCurrentMinute(time.get(Calendar.MINUTE));
}
automaticBackupType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
AutomaticBackupType tmpAutomaticBackupType = (AutomaticBackupType)automaticBackupType.getSelectedItem();
updateVisibility(tmpAutomaticBackupType);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.automatic_backup_configuration_title)
.setView(view)
.setPositiveButton(R.string.btn_save, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// Do nothing here because we override this button later to change the close behaviour.
// However, we still need this because on older versions of Android unless we
// pass a handler the button doesn't get instantiated
}
})
.setNegativeButton(R.string.btn_cancel, null)
.create();
}
@Override
public void onStart() {
super.onStart();
AlertDialog d = (AlertDialog)getDialog();
if (d != null) {
Button positiveButton = d.getButton(Dialog.BUTTON_POSITIVE);
positiveButton.setOnClickListener(v -> {
// save the data
});
}
}
private void updateVisibility(AutomaticBackupType tmpAutomaticBackupType) {
if (tmpAutomaticBackupType != null) {
UiHelper.setVisibility(timePart, !tmpAutomaticBackupType.equals(AutomaticBackupType.disabled), true);
}
}
}
刚刚找到解决方案(我不喜欢它,但它有效)。
我把它贴在这里以防其他人需要它。
TimePicker 似乎有 Positive/Natural/Negative 个与 AlertDialog 具有相同 ID 的按钮。当我使用对话框生成器设置它们时,它实际上在 TimePicker 中设置了一个。
一般情况下,解决方法是在Dialog中添加一个没有TimePicker视图的视图,设置好按钮后才添加。
上面的代码在两个位置发生了变化: 首先在onCreateDialog方法中时间设置块:
{
// time
this.time.setIs24HourView(DateFormat.is24HourFormat(getActivity()));
this.time.setCurrentHour(time.get(Calendar.HOUR_OF_DAY));
this.time.setCurrentMinute(time.get(Calendar.MINUTE));
timePart.removeView(this.time); // remove the TimePicker view from the layout
}
onStart 方法中的第二个
public void onStart() {
super.onStart();
AlertDialog d = (AlertDialog)getDialog();
if (d != null) {
Button positiveButton = d.getButton(Dialog.BUTTON_POSITIVE);
timePart.addView(time); // Now we can add the view
positiveButton.setOnClickListener(v -> {
// save the data
});
}
}