I am creating a dialog with custom view. One of the component in the view is a TimePicker. When doing that, the OK+Cancel buttons of the TimePicker take control and the FragmentDialog default button are disable.
This cause a problem since depending on user data I show/hide the TimePicker view and when it is hidden it also hide the OK+Cancel buttons.
It is important to have a custom view, so the user will be able to select all relevant data in a single view.
Edit - Add image that shows the problem
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>
To show the dialog I use the code:
new AutomaticBackupConfigurationAlert().show(getSupportFragmentManager(), "TAG");
The AutomaticBackupConfigurationAlert code:
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);
}
}
}
Just found a solution (I do not like it, but it works).
I am posting it here in case someone else will need it.
It seems that TimePicker has the Positive/Natural/Negative buttons with the same id as the AlertDialog. When I set them using the Dialog builder, it actually set the one in the TimePicker.
In general, the solution is to add a view to the Dialog without the TimePicker view and add it only after the buttons are set.
The code above changed in two location: First in onCreateDialog method in the time settings block:
{
// 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
}
Second in the onStart method
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
});
}
}