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