【问题标题】:Appcompatactivity with custom native (not compatibility) dialogpreference containing a TimePickerAppcompatactivity 与包含 TimePicker 的自定义本机(不兼容)对话框首选项
【发布时间】:2017-07-06 08:01:15
【问题描述】:

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

DialogPreference 必须是“native”,这意味着不是herehere 中描述的兼容版本。

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

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

preferences_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 中实例化的,可以从构造函数中记录下来。也没有编译时错误,没有运行时错误。

【问题讨论】:

    标签: android settings android-timepicker preferencescreen dialog-preference


    【解决方案1】:

    最后,我找到了一种不同的方法,它看起来很干净,经过测试并且适用于从 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");
        }
    }
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-12-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-03
      相关资源
      最近更新 更多