自定义首选项在“首选项"屏幕上显示的方式与本机“首选项"不同 [英] Custom Preference shows differently on Preference screen than native Preferences

查看:139
本文介绍了自定义首选项在“首选项"屏幕上显示的方式与本机“首选项"不同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一张图片值一千字,这是我的问题:

A picture is worth a thousand words, this is my problem:

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

The last three preferences are a custom time picker for minutes and seconds The other settings are the normal SwitchPreference, RingTonePreference, ListPreference and EditTextPreference

这是我的首选项XML

This is my preferences 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代码,因为它很长.
我没有在代码的任何地方设置首选项布局,因此我希望它的显示方式与内置的首选项相同,但是如上图所示,它是不同的.

I don't put here the TimePickerPreference code because it is long.
I don't set the preference layout anywhere in the code, so I expected to be presented the same as the built-in preferences, but as can be seen in the picture above, it is different.

关于为什么显示自定义首选项不同于常规首选项的任何想法?

Any ideas on why the custom preference item is presented different from the regular preferences?

更多信息,根据要求

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

推荐答案

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

The problem was/is in the third argument (defStyleAtttr) to the preference constructor.

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

It is an integer, but 0 is not the default value, com.android.internal.R.attr.dialogPreferenceStyle is.

所以我删除了我的第三个构造函数;并让基类DialogPreference处理defStylAttr值.现在,它显示出与其他内置首选项相同的样式.

So I deleted my third constructor; and let the base class DialogPreference deal with the defStylAttr value. And now it shows teh same style as the other built-in preferences.

这篇关于自定义首选项在“首选项"屏幕上显示的方式与本机“首选项"不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆