1

我正在 Android 中构建首选项/设置屏幕AppCompatActivity。一个要求是[DialogPreference][1]带有TimePicker.

DialogPreference 必须是“本机”,这意味着不是此处此处描述的兼容版本。

AppCompatActivity 的代码:

...

public class SettingsActivity extends AppCompatActivity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_preferences);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_settings);
        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    }
}

activity_preferences.xml 的布局:

...

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <fragment
            android:name="nl.waywayway.broodkruimels.SettingsFragment"
            android:id="@+id/settings_fragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </android.support.v4.widget.NestedScrollView>

SettingsFragment 类:

...

public class SettingsFragment extends PreferenceFragment
{
    Context mContext;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);
    }
}

首选项.xml 文件:

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

    <SwitchPreference
        android:key="pref_notify"
        android:title="@string/pref_notify"
        android:summary="@string/pref_notify_summ"
        android:defaultValue="false" />

    <nl.waywayway.broodkruimels.TimePreference
        android:dependency="pref_notify"
        android:key="pref_notify_time"
        android:title="@string/pref_notify_time"
        android:summary="@string/pref_notify_time_summ"
        android:defaultValue="390" />

</PreferenceScreen>

和自定义 TimePreference 类:

public class TimePreference extends DialogPreference
{
    private TimePicker mTimePicker = null;
    private int mTime;
    private int mDialogLayoutResId = R.layout.preferences_timepicker_dialog;

    // 4 constructors for the API levels,
    // calling each other

    public TimePreference(Context context)
    {
        this(context, null);
    }

    public TimePreference(Context context, AttributeSet attrs)
    {
        this(context, attrs, R.attr.preferenceStyle);
    }

    public  TimePreference(Context context, AttributeSet attrs, int defStyleAttr)
    {
        this(context, attrs, defStyleAttr, defStyleAttr);
    }

    public  TimePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
    {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public int getTime()
    {
        return  mTime;
    }

    public void setTime(int time)
    {
        mTime = time;

        // Save to Shared Preferences
        persistInt(time);
    }

    @Override
    public int getDialogLayoutResource()
    {
        return mDialogLayoutResId;
    }

    @Override
    protected Object onGetDefaultValue(TypedArray a, int index)
    {
        //  Default  value  from  attribute.  Fallback  value  is  set  to  0.
        return a.getInt(index,  0);
    }

    @Override
    protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue)
    {
        // Read the value. Use the default value if it is not possible.
        setTime(restorePersistedValue ?
                getPersistedInt(mTime) : (int) defaultValue);
    }

    @Override
    protected void onBindDialogView(View view)
    {
        super.onBindDialogView(view);

        mTimePicker = (TimePicker) view.findViewById(R.id.preferences_timepicker);

        if (mTimePicker == null)
        {
            throw new IllegalStateException("Dialog view must contain a TimePicker with id 'preferences_timepicker'");
        }

        // Get the time from the related Preference
        Integer minutesAfterMidnight = null;
        TimePreference preference = (TimePreference) findPreferenceInHierarchy("pref_notify_time");
        minutesAfterMidnight = preference.getTime();

        // Set the time to the TimePicker
        if (minutesAfterMidnight != null)
        {
            int hours = minutesAfterMidnight / 60;
            int minutes = minutesAfterMidnight % 60;
            boolean is24hour = DateFormat.is24HourFormat(getContext());

            mTimePicker.setIs24HourView(is24hour);

            if (Build.VERSION.SDK_INT >= 23)
            {
                mTimePicker.setHour(hours);
                mTimePicker.setMinute(minutes);
            }
            else
            {
                mTimePicker.setCurrentHour(hours);
                mTimePicker.setCurrentMinute(minutes);
            }
        }
    }

    @Override
    protected void onDialogClosed(boolean positiveResult)
    {
        if (positiveResult)
        {
            // Get the current values from the TimePicker
            int hours;
            int minutes;
            if (Build.VERSION.SDK_INT >= 23)
            {
                hours = mTimePicker.getHour();
                minutes = mTimePicker.getMinute();
            }
            else
            {
                hours = mTimePicker.getCurrentHour();
                minutes = mTimePicker.getCurrentMinute();
            }

            // Generate value to save
            int minutesAfterMidnight = (hours * 60) + minutes;

            // Save the value
            TimePreference timePreference = (TimePreference) findPreferenceInHierarchy("pref_notify_time");

            // This allows the client to ignore the user value.
            if (timePreference.callChangeListener(minutesAfterMidnight))
            {
                // Save the value
                timePreference.setTime(minutesAfterMidnight);
            }
        }
    }
}

options_timepicker_dialog.xml 文件如下:

...
<TimePicker 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/preferences_timepicker"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

结果就像下面的屏幕截图。在装有 Android 7 的 Moto G5 plus 手机上。

截屏

问题: 应该有两个偏好。但是,自定义 DialogPreference 未显示在设置列表中。这里出了什么问题?AppCompatActivity 是否真的与“本机”DialogPreference 一起使用?

TimePreference 类实际上是从首选项 xml 中实例化的,可以从构造函数中记录下来。也没有编译时错误,没有运行时错误。

4

1 回答 1

0

最后,我找到了一种不同的方法,它看起来很干净,经过测试并且适用于从 Android 4 到 7 的真实设备。首选项显示在首选项屏幕中。

此外,TimePicker 对话框以横向正确显示。这是某些设备上的问题。看

步骤是:

偏好活动:

...
public class SettingsActivity extends AppCompatActivity
{
    public static final String KEY_PREF_NOTIFY = "pref_notify";
    public static final String KEY_PREF_NOTIFY_TIME = "pref_notify_time";

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_preferences);
    }
}

布局文件activity_preferences.xml:

...

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <fragment
            android:name="nl.waywayway.broodkruimels.SettingsFragment"
            android:id="@+id/settings_fragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </android.support.v4.widget.NestedScrollView>

SettingsFragment 类:

...
public class SettingsFragment extends PreferenceFragmentCompat
{
    Context mContext;

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey)
    {
        setPreferencesFromResource(R.xml.preferences, rootKey);
    }

    // The Context object of this fragment is only available when this fragment is 'attached', so set the Context object inside the onAttach() method
    @Override
    public void onAttach(Context context)
    {
        super.onAttach(context);
        mContext = context;
    }

    // This method sets the action of clicking the Preference
    @Override
    public boolean onPreferenceTreeClick(Preference preference)
    {
        switch (preference.getKey())
        {
            case SettingsActivity.KEY_PREF_NOTIFY_TIME:
                showTimePickerDialog(preference);
                break;
        }

        return super.onPreferenceTreeClick(preference);
    }

    private void showTimePickerDialog(Preference preference)
    {
        DialogFragment newFragment = new TimePickerFragment();
        newFragment.show(getFragmentManager(), "timePicker");
    }
}

首选项.xml 文件:

...
<android.support.v7.preference.PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.preference.SwitchPreferenceCompat
        android:key="pref_notify"
        android:title="@string/pref_notify"
        android:summary="@string/pref_notify_summ"
        android:defaultValue="false" />

    <android.support.v7.preference.Preference
        android:dependency="pref_notify"
        android:key="pref_notify_time"
        android:title="@string/pref_notify_time"
        android:summary="@string/pref_notify_time_summ"
        android:defaultValue="390" />

</android.support.v7.preference.PreferenceScreen>

TimePickerFragment 类,请参阅 Android 'Pickers' 指南 ( https://developer.android.com/guide/topics/ui/controls/pickers.html ) 以获得解释:

...
public class TimePickerFragment extends DialogFragment
    implements TimePickerDialog.OnTimeSetListener
{
    private Context mContext;
    private int mTime; // The time in minutes after midnight

    // The Context object for this fragment is only available when this fragment is 'attached', so set the Context object inside the onAttach() method
    @Override
    public void onAttach(Context context)
    {
        super.onAttach(context);
        mContext = context;
    }

    // Getter and setter for the time
    public int getTime()
    {
        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(mContext);
        int prefDefault = mContext.getResources().getInteger(R.integer.preferences_time_default);
        mTime = sharedPref.getInt(SettingsActivity.KEY_PREF_NOTIFY_TIME, prefDefault);

        return  mTime;
    }

    public void setTime(int time)
    {
        mTime = time;

        // Save to Shared Preferences
        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(mContext);
        SharedPreferences.Editor editor = sharedPref.edit();
        editor.putInt(SettingsActivity.KEY_PREF_NOTIFY_TIME, time);
        editor.commit();
    }

    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
        int minutesAfterMidnight = getTime();
        int hour = minutesAfterMidnight / 60;
        int minute = minutesAfterMidnight % 60;

        Log.i("HermLog", "onCreateDialog(), tijd: " + hour + ":" + minute);

        // Create a new instance of TimePickerDialog and return it
        return new TimePickerDialog(
            mContext, 
            this, 
            hour, 
            minute,
            DateFormat.is24HourFormat(mContext)
        );
    }

    @Override
    public void onTimeSet(TimePicker view, int hour, int minute)
    {
        int minutesAfterMidnight = (hour * 60) + minute;
        setTime(minutesAfterMidnight);      
    }
}
于 2017-07-11T11:51:17.327 回答