自定义首选项在首选项屏幕上的显示方式与本机首选项不同

时间:2017-09-26 08:34:56

标签: android android-preferences preferencescreen

一张图片胜过千言万语,这是我的问题:

My settings preference screen

最后三个首选项是分钟和秒的自定义时间选择器 其他设置是正常的SwitchPreference,RingTonePreference,ListPreference和EditTextPreference

这是我的偏好XML

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

    <PreferenceCategory android:title="@string/pref_header_general">

        <SwitchPreference
            android:defaultValue="true"
            android:key="@string/pref_key_turbo"
            android:summaryOff="@string/pref_summaryOff_turbo"
            android:summaryOn="@string/pref_summaryOn_turbo"
            android:title="@string/pref_title_turbo" />

        <ListPreference
            android:defaultValue="1"
            android:entries="@array/pref_distance_units_list_titles"
            android:entryValues="@array/pref_distance_units_list_values"
            android:key="@string/pref_key_distance_units"
            android:negativeButtonText="@null"
            android:positiveButtonText="@null"
            android:title="@string/pref_title_distance_units" />

    </PreferenceCategory>

    <PreferenceCategory android:title="@string/pref_header_advanced">

        <EditTextPreference
            android:defaultValue="100"
            android:dialogMessage="@string/pref_distance_dialog_msg"
            android:dialogTitle="@string/pref_distance_dialog_title"
            android:inputType="number|numberDecimal"
            android:key="@string/pref_key_distance"
            android:summary="@string/pref_summary_distance"
            android:title="@string/pref_title_distance" />

        <!-- Allows the user to choose a ringtone -->
        <RingtonePreference
            android:defaultValue="content://settings/system/notification_sound"
            android:key="@string/pref_key_default_ringtone"
            android:ringtoneType="notification"
            android:showDefault="true"
            android:showSilent="true"
            android:title="@string/pref_title_default_ringtone" />

        <com.test.birenbaum.TimePickerPreference
            android:defaultValue="@string/pref_default_first"
            android:dialogMessage="@string/pref_default_first_dialog_msg"
            android:dialogTitle="@string/pref_default_first_dialog_title"
            android:key="@string/pref_key_default_first"
            android:summary="@string/pref_summary_default_first"
            android:title="@string/pref_title_default_first" />

        <com.test.birenbaum.TimePickerPreference
            android:defaultValue="@string/pref_default_value_retry"
            android:dialogMessage="@string/pref_default_retry_dialog_msg"
            android:dialogTitle="@string/pref_default_retry_dialog_title"
            android:key="@string/pref_key_default_retry"
            android:summary="@string/pref_summary_default_retry"
            android:title="@string/pref_title_default_retry" />

        <com.test.birenbaum.TimePickerPreference
            android:defaultValue="@string/pref_default_value_off"
            android:dialogMessage="@string/pref_default_off_dialog_msg"
            android:dialogTitle="@string/pref_default_off_dialog_title"
            android:key="@string/pref_key_default_off"
            android:summary="@string/pref_summary_default_off"
            android:title="@string/pref_title_default_off" />

        <ListPreference
            android:defaultValue="1"
            android:entries="@array/pref_default_algo_list_titles"
            android:entryValues="@array/pref_default_algo_list_values"
            android:key="@string/pref_key_default_algo_mode"
            android:negativeButtonText="@null"
            android:positiveButtonText="@null"
            android:summary="@string/pref_summary_default_algo"
            android:title="@string/pref_title_default_algo_mode" />
    </PreferenceCategory>
</PreferenceScreen>

我没有把TimePickerPreference代码放在这里,因为它很长 我没有在代码中的任何地方设置首选项布局,所以我希望与内置首选项一样,但是如上图所示,它是不同的。

有关自定义偏好项目的原因与常规偏好不同的任何想法?

按要求提供更多信息

public class TimePickerPreference extends DialogPreference {
    private static final String TAG = "TimePickerPreference";
    public static boolean DEBUG = true;

    private static final String DEFAULT_VALUE = "0m0s";
    private static final String DEFAULT_SUMMARY = "%s";
    private static final String SPLIT_REGEX = "m|s";
    private static final String MATCH_REGEX = "\\d+m[0-5]?\\ds";
    private static final String TIME_FORMAT = "%dm%ds";

    private static final int MAX_MINUTES = 30;
    private static final int MIN_MINUTES = 0;
    private static final int MAX_SECONDS = 59;
    private static final int MIN_SECONDS = 0;

    private int mSeconds;
    private int mMinutes;

    private NumberPicker mSecondsPicker;
    private NumberPicker mMinutesPicker;

    private String mDefaultValue;
    private String mSummary;
    private String mSummaryFormat;


    public TimePickerPreference(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        MyLog.pe(DEBUG, TAG, "+ Constructor TimePickerPreference(context:%s, attrs:%s, defStyleAttr:%d)", context, attrs, defStyleAttr);

        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.TimePickerPreference, 0, 0);

        setTitle(a.getString(R.styleable.TimePickerPreference_android_title));
        if (getTitle() == null) {
            setTitle(TimePickerPreference.class.getSimpleName());
        }

        mSummary = a.getString(R.styleable.TimePickerPreference_android_summary);
        if (mSummary == null) {
            mSummary = DEFAULT_SUMMARY;
        }
        setSummary(mSummary);
        // At this stage the summary is virgin, still in skeleton format (with %s)
        setSummaryFormat(mSummary);

        mDefaultValue = a.getString(R.styleable.TimePickerPreference_android_defaultValue);
        if (mDefaultValue == null) {
            mDefaultValue = DEFAULT_VALUE;
        }

        setDefaultValue(mDefaultValue);

        a.recycle();

        setDialogLayoutResource(R.layout.preference_dialog_timepicker);
        setPositiveButtonText(R.string.save_button);
        setNegativeButtonText(android.R.string.cancel);

        MyLog.px(DEBUG, TAG, "- Constructor TimePickerPreference()");
    }

    public TimePickerPreference(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TimePickerPreference(Context context) {
        this(context, null, 0);
    }

    @Override
    protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
        MyLog.pe(DEBUG, TAG, "+ onSetInitialValue(restorePersistedValue:%s, defaultValue:%s)", restorePersistedValue, defaultValue);
        String[] time;
        if (restorePersistedValue) {
            time = getPersistedString((String) defaultValue).split(SPLIT_REGEX);
            // Assume persisted value is kosher
            mMinutes = Integer.valueOf(time[0]);
            mSeconds = Integer.valueOf(time[1]);

        } else {
            time = ((String) defaultValue).split(SPLIT_REGEX);
            if (time.length == 2) {
                // Enforce MIN-MAX boundaries
                mMinutes = Math.max(MIN_MINUTES, Math.min(MAX_MINUTES, (Integer.valueOf(time[0]))));
                mSeconds = Math.max(MIN_SECONDS, Math.min(MAX_SECONDS, (Integer.valueOf(time[1]))));
                defaultValue = getTime();
                persistString((String) defaultValue);
            } else {
                // Picker system default value, definitely kosher
                time = DEFAULT_VALUE.split(SPLIT_REGEX);
                mMinutes = Integer.valueOf(time[0]);
                mSeconds = Integer.valueOf(time[1]);
                persistString(DEFAULT_VALUE);
            }
        }
        MyLog.px(DEBUG, TAG, "- onSetInitialValue()");
    }

    @Override
    protected Object onGetDefaultValue(TypedArray a, int index) {
        MyLog.pe(DEBUG, TAG, "* onGetDefaultValue(a:%s, index:%s)", a, index);
        return a.getString(index);
    }

//    @Override
//    protected View onCreateDialogView() {
//        return super.onCreateDialogView();
//    }

    @Override
    protected void onBindDialogView(View view) {
        MyLog.pe(DEBUG, TAG, "+ onBindDialogView(view:%s)", view);
        super.onBindDialogView(view);

        TextView tvMessage = view.findViewById(R.id.tvMessage);
        String message = (String) getDialogMessage();
        if (message == null || message.isEmpty()) {
            tvMessage.setVisibility(View.GONE);
        } else {
            tvMessage.setText(message);
        }

        mMinutesPicker = view.findViewById(R.id.minutesPicker);
        mMinutesPicker.setMaxValue(MAX_MINUTES);
        mMinutesPicker.setMinValue(MIN_MINUTES);

        mSecondsPicker = view.findViewById(R.id.secondsPicker);
        mSecondsPicker.setMaxValue(MAX_SECONDS);
        mSecondsPicker.setMinValue(MIN_SECONDS);

        mMinutesPicker.setValue(mMinutes);
        mSecondsPicker.setValue(mSeconds);


        MyLog.px(DEBUG, TAG, "- onBindDialogView()");
    }

    @Override
    protected void onDialogClosed(boolean positiveResult) {
        MyLog.pe(DEBUG, TAG, "+ onDialogClosed(positiveResult:%s)", positiveResult);
        super.onDialogClosed(positiveResult);

        if (positiveResult) {
            String newValue = new StringBuilder()
                    .append(mMinutesPicker.getValue())
                    .append('m')
                    .append(mSecondsPicker.getValue())
                    .append('s')
                    .toString();
            if (callChangeListener(newValue)) {
                //noinspection ConstantConditions
                setTime(newValue);
            }
        }
        MyLog.px(DEBUG, TAG, "- onDialogClosed()");
        super.onDialogClosed(positiveResult);
    }

    @Override
    public void setDefaultValue(Object defaultValue) {
        MyLog.pe(DEBUG, TAG, "* setDefaultValue(defaultValue:%s)", defaultValue);
        super.setDefaultValue(defaultValue);
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        MyLog.pe(DEBUG, TAG, "+ onRestoreInstanceState(state:%s)",state);
        if (state == null || !state.getClass().equals(SavedState.class)) {
            // Didn't save state for us in onSaveInstanceState
            super.onRestoreInstanceState(state);
        }
        MyLog.px(DEBUG, TAG, "- onRestoreInstanceState()");
    }

    /**
     * Save the instance state so that it will survive events like
     * screen orientation change that may temporarily destroy it.
     */
    @Override
    protected Parcelable onSaveInstanceState() {
        MyLog.pe(DEBUG, TAG, "+ onSaveInstanceState()");
        final Parcelable superState = super.onSaveInstanceState();
        Parcelable result;
        if (isPersistent()) {
            // No need to save instance state since it's persistent
            result = superState;
        } else {
            final SavedState myState = new SavedState(superState);
            myState.seconds = mSeconds;
            myState.minutes = mMinutes;
            result = myState;
        }
        MyLog.px(DEBUG, TAG, "- onSaveInstanceState()");
        return result;
    }

    //----------------------------------------------------------------------------

    public int getSeconds() {
        return mSeconds;
    }

    public void setSeconds(int seconds) {
        mSeconds = seconds;
    }

    public int getMinutes() {
        return mMinutes;
    }

    public void setMinutes(int minutes) {
        mMinutes = minutes;
    }

    public String getSummaryFormat() {
        return mSummaryFormat;
    }

    public void setSummaryFormat(String summaryFormat) {
        mSummaryFormat = summaryFormat;
    }

    @SuppressLint("DefaultLocale")
    public String getTime() {
        return String.format(TIME_FORMAT, mMinutes, mSeconds);
    }

    /**
     * Saves the value to the {@link android.content.SharedPreferences SharedPreferences}.
     *
     * @param newTime A value to save. Must be in the correct format otherwise the save
     *                operation is not executed
     */
    public void setTime(String newTime) {
        MyLog.pe(DEBUG, TAG, "+ setTime(newTime:%s)", newTime);
        final boolean wasBlocking = shouldDisableDependents();

        if (newTime.matches(MATCH_REGEX)) {
            String[] time = newTime.split(SPLIT_REGEX);
            // No need to check for MAX and MIN values
            // The values come from the spinners, therefore are within boundaries
            mMinutes = Integer.valueOf(time[0]);
            mSeconds = Integer.valueOf(time[1]);
            persistString(newTime);
            notifyChanged();
        }

        final boolean isBlocking = shouldDisableDependents();
        if (isBlocking != wasBlocking) {
            notifyDependencyChange(isBlocking);
        }
        MyLog.px(DEBUG, TAG, "- setTime()");
    }

    @Override
    public String toString() {
        return getTime();
    }

//----------------------------------------------------------------------------

    /**
     * Returns the summary of this TimePickerPreference. If the summary
     * has a {@linkplain java.lang.String#format String formatting}
     * marker in it (i.e. "%s" or "%1$s"), then the current minutes and seconds
     * value will be substituted in its place.
     *
     * @return the summary with appropriate string substitution
     */
    @Override
    public CharSequence getSummary() {
        MyLog.pe(DEBUG, TAG, "+ getSummary()");
        CharSequence result;
        if (mSummary == null) {
            result = super.getSummary();
        } else {
            result = String.format(mSummary, getTime());
        }
        MyLog.px(DEBUG, TAG, "- getSummary()");
        return result;
    }

    /**
     * Sets the summary for this Preference with a CharSequence.
     * If the summary has a {@linkplain java.lang.String#format String formatting}
     * marker in it (i.e. "%s" or "%1$s"), then the current entry value will be substituted
     * in its place when it's retrieved.
     *
     * @param summary The summary for the preference.
     */
    @Override
    public void setSummary(CharSequence summary) {
        MyLog.pe(DEBUG, TAG, "+ setSummary(summary:%s)", summary);
        super.setSummary(summary);
        if (summary == null && mSummary != null) {
            mSummary = null;
        } else if (summary != null && !summary.equals(mSummary)) {
            mSummary = summary.toString();
        }
        MyLog.px(DEBUG, TAG, "- setSummary()");
    }


    //----------------------------------------------------------------------------

    private static class SavedState extends BaseSavedState {
        int seconds;
        int minutes;

        public SavedState(Parcelable superState) {
            super(superState);
        }

        public SavedState(Parcel source) {
            super(source);
            MyLog.pe(DEBUG, TAG, "+ SavedState(source:%s)", source);

            seconds = source.readInt();
            minutes = source.readInt();

            MyLog.px(DEBUG, TAG, "- SavedState()");
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            MyLog.pe(DEBUG, TAG, "+ writeToParcel(dest:%s, flags:%s)", dest, flags);
            super.writeToParcel(dest, flags);

            dest.writeInt(seconds);
            dest.writeInt(minutes);

            MyLog.px(DEBUG, TAG, "- writeToParcel()");
        }

        @SuppressWarnings("unused")
        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
            @Override
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            @Override
            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }
}

1 个答案:

答案 0 :(得分:0)

问题是/是在偏好构造函数的第三个参数(defStyleAtttr)中。

这是一个整数,但0不是默认值, com.android.internal.R.attr.dialogPreferenceStyle 是。

所以我删除了我的第三个构造函数;并让基类DialogPreference处理defStylAttr值。现在它显示出与其他内置偏好相同的风格。