代码之家  ›  专栏  ›  技术社区  ›  ilomambo

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

  •  1
  • ilomambo  · 技术社区  · 8 年前

    一幅画抵得上千言万语,这是我的问题:

    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 回复  |  直到 8 年前
        1
  •  3
  •   ilomambo    8 年前

    问题在第三个论点中( defStyleAtttr )到首选项构造函数。

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

    DialogPreference 处理 defStylAttr 价值现在它显示了与其他内置首选项相同的样式。