SeekBar 首选项
如何使用 SeekBar 构建首选项小部件。
引言
在本文中,我将尝试展示如何创建自己的带有 SeekBar
的偏好设置小部件。

背景
Android 提供了一些方便的小部件,例如 CheckBoxPreference
、EditTextPreference
、ListPreference
来构建漂亮的偏好设置屏幕。但是,如果现有的小部件不能满足您的要求,您可以轻松地基于现有的小部件创建自己的小部件。
通常,一些整数偏好设置具有有意义的限制,例如音量或亮度级别。在这种情况下,构建您自己的小部件来管理这些偏好设置,然后在应用程序中重复使用该小部件是有意义的。
准备工作
DialogPreference
是此类小部件的最佳超类。它提供了一个偏好设置,该偏好设置在偏好设置列表中显示两行文本(标题和摘要),并在单击时显示带有“确定”/“取消”按钮的对话框。

我们称它为 SeekBarPreference
。此小部件的参数可以是最小值、最大值和当前默认值。实际的当前值将通过给定的键存储在关联的共享偏好设置中。
在这种情况下,文件 /res/xml/preferences.xml 可能看起来像这样
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:example="http://schemas.android.com/apk/res/com.mnm.seekbarpreference">
<com.mnm.seekbarpreference.SeekBarPreference
android:key="seekBarPreference"
android:title="@string/dialog_title"
android:dialogTitle="@string/dialog_title"
android:summary="@string/summary"
android:persistent="true"
android:defaultValue="20"
example:minValue="10"
example:maxValue="50" />
</PreferenceScreen>
现有标签可用于设置默认当前值。但是最小值和最大值需要它们自己的标签。为此,/res/values/attrs.xml 文件应使用以下声明进行扩展
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="com.mnm.seekbarpreference.SeekBarPreference">
<attr name="minValue" format="integer" />
<attr name="maxValue" format="integer" />
</declare-styleable>
</resources>
<declare-styleable> 标签的 Name
属性应包含限定的小部件类名。
在 preferences.xml 中应以更完整的格式(http://schemas.android.com/apk/res/qualified_class_name)定义相同的名称,作为附加的命名空间(见上文)。
使用 XML 的最后阶段是为在小部件单击时启动的对话框创建布局。这是一个非常常见的代码,可以省略而不会产生任何影响。只需提到它包含最小值、最大值和当前值的 TextViews
以及 SeekBar
。
现在,我们可以继续实现 SeekBarPreference
类。
实现
首先,所有提到的属性都应在小部件构造函数中读取
mMinValue = attrs.getAttributeIntValue
(PREFERENCE_NS, ATTR_MIN_VALUE, DEFAULT_MIN_VALUE);
mMaxValue = attrs.getAttributeIntValue
(PREFERENCE_NS, ATTR_MAX_VALUE, DEFAULT_MAX_VALUE);
mDefaultValue = attrs.getAttributeIntValue
(ANDROID_NS, ATTR_DEFAULT_VALUE, DEFAULT_CURRENT_VALUE);
其中常量是命名空间、属性和属性值的默认值(如果在偏好设置声明中省略它们)。
private static final String PREFERENCE_NS =
"http://schemas.android.com/apk/res/com.mnm.seekbarpreference";
private static final String ANDROID_NS = "http://schemas.android.com/apk/res/android";
private static final String ATTR_DEFAULT_VALUE = "defaultValue";
private static final String ATTR_MIN_VALUE = "minValue";
private static final String ATTR_MAX_VALUE = "maxValue";
对于对话框设置,应重写 onCreateDialogView
方法
@Override
protected View onCreateDialogView() {
// Get current value from settings
mCurrentValue = getPersistedInt(mDefaultValue);
// Inflate layout
LayoutInflater inflater =
(LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.dialog_slider, null);
// Put minimum and maximum
((TextView) view.findViewById(R.id.min_value)).setText(Integer.toString(mMinValue));
((TextView) view.findViewById(R.id.max_value)).setText(Integer.toString(mMaxValue));
// Setup SeekBar
mSeekBar = (SeekBar) view.findViewById(R.id.seek_bar);
mSeekBar.setMax(mMaxValue - mMinValue);
mSeekBar.setProgress(mCurrentValue - mMinValue);
mSeekBar.setOnSeekBarChangeListener(this);
// Put current value
mValueText = (TextView) view.findViewById(R.id.current_value);
mValueText.setText(Integer.toString(mCurrentValue));
return view;
}
当前值是通过 preferences.xml 中为特定小部件实例声明的键获取的。此外,在 SeekBar
设置期间,应考虑到它的最小值是零。这就是为什么当偏好设置的最小值与零不同时使用减法的原因。顺便说一句,此代码仅适用于非负数,并且当最大值确实大于最小值时才是正确的。
完成上一步后,可以启动应用程序,用户可以拖动拇指,但当前值标签上的文本不会更改,因为应该引入位置更改处理程序。
为此,SeekBarPreference
应该实现 OnSeekBarChangeListener
接口。在前面的代码中,正是这个接口通过“mSeekBar.setOnSeekBarChangeListener(this);
”调用传递给 SeekBar
。应该只实现三种可能的方法中的一种
public void onProgressChanged(SeekBar seek, int value, boolean fromTouch) {
mCurrentValue = value + mMinValue;
mValueText.setText(Integer.toString(mCurrentValue));
}
再次,应该使用加法,因为 SeekBar
最小值是零。
下一步是持久化更改后的值。在对话框关闭时,它会调用 onDialogClosed
方法,该方法应该被重写
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (!positiveResult) {
return;
}
if (shouldPersist()) {
persistInt(mCurrentValue);
}
notifyChanged();
}
在肯定结果中,该值是持久化的,并且 shouldPersist()
检查分析根据来自 preferences.xml 的标志 android:persistent
是否有必要这样做。
最后一行是用于一个小技巧。关键是默认情况下,小部件的摘要行是 static
的,如果它应该反映当前值,则应添加以下代码
@Override
public CharSequence getSummary() {
String summary = super.getSummary().toString();
int value = getPersistedInt(mDefaultValue);
return String.format(summary, value);
}
这里,在摘要请求时,原始 string
被视为一个模板,用于将当前值放入其中。它在偏好设置屏幕打开时完美运行。但是要在通过对话框更改偏好设置后获得相同的行为,应该调用 notifyChanged()
。
结论
实现的窗口小部件适用于各种首选项,并且优雅地扩展了现有的首选项窗口小部件集。动态摘要行方法可以在不同的首选项窗口小部件中使用。
链接
历史
- 2011 年 3 月 1 日:初始帖子