以前使用android.preference包里面的类创建出来的偏好设置界面是没有兼容低版本的,后来发现com.android.support:preference库有对低版本做兼容,在低版本上面也可以有Material Design风格,于是对其作了一番研究。
1、应用的主题需要继承自Theme.AppCompat系列:
<style name="AppBaseTheme" parent="Theme.AppCompat.Light.NoActionBar">
</style>
2、在build.gradle中添加以下依赖:
compile 'com.android.support:preference-v7:26.1.0'
compile 'com.android.support:preference-v14:26.1.0'
3、在应用的主题里面添加:
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
4、编写my_pref.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:key="preference_screen_settings"
android:title="settings">
<android.support.v7.preference.PreferenceCategory android:title="Category">
<android.support.v7.preference.SwitchPreferenceCompat
android:defaultValue="true"
android:key="key_1"
android:summary="Summary"
android:title="Title" />
<android.support.v7.preference.EditTextPreference
android:defaultValue="default_value"
android:dialogMessage="dialog_message"
android:key="key_2"
android:summary="Summary"
android:title="Title" />
<android.support.v7.preference.CheckBoxPreference
android:defaultValue="true"
android:key="key_3"
android:summary="Summary"
android:title="Title" />
<android.support.v7.preference.ListPreference
android:defaultValue="2"
android:entries="@array/list_preference_entries"
android:entryValues="@array/list_preference_entry_values"
android:key="key_4"
android:summary="Summary"
android:title="Title" />
<android.support.v7.preference.SeekBarPreference
android:defaultValue="20"
android:key="key_5"
android:max="1000"
android:min="0"
android:summary="Summary"
android:title="Title" />
</android.support.v7.preference.PreferenceCategory>
</android.support.v7.preference.PreferenceScreen>
5、编写承载偏好设置的Fragment,这里继承自PreferenceFragmentCompat类:
public class PreferenceFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.my_pref);
}
}
效果图:
6、对Preference节点进行删除、添加和修改:
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.my_pref);
PreferenceCategory category = (PreferenceCategory) findPreference("category");
category.removePreference(findPreference("key_3"));
ListPreference listPreference = new ListPreference(getContext());
listPreference.setEntries(new String[]{"A", "B", "C", "D"});
listPreference.setEntryValues(new String[]{"A", "B", "C", "D"});
listPreference.setSummary("Summary");
listPreference.setTitle("Title");
listPreference.setDialogTitle("DialogTitle");
listPreference.setKey("key_6");
listPreference.setDefaultValue("C");
category.addPreference(listPreference);
Preference preference1 = findPreference("key_1");
preference1.setTitle("AAAAA");
}
7、修改某个Preference的点击事件:
Preference preference2 = findPreference("key_2");
preference2.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
//TODO
return false;
}
});
8、自定义ListPreference,可以自定义一个attrs,从XML文件中把属性值传进来;重写onBindViewHolder方法可以通过holder拿到对应的itemView,这样便可以对itemView里面的内容进行修改,这里我的itemView是自定义的,布局通过android:layout="@layout/item_view"进行设置:
public class MyListPreference extends ListPreference {
private String titleText;
public MyListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyListPreference);
titleText = typedArray.getString(R.styleable.MyListPreference_title_text);
typedArray.recycle();
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
View view = holder.itemView;
TextView tvTitle= (TextView) view.findViewById(R.id.tv_title_text);
tvTitle.setText(titleText);
}
}
9、对CheckBoxPreference和SwitchPreference的自定义类似,如果只想修改右边的控件(将Switch修改成自定义的View)可以在对应的Preference中添加android:widgetLayout="@layout/switch_layout",再自定义一个类继承自SwitchPreferenceCompat:
public class MySwitchPreferenceCompat extends SwitchPreferenceCompat {
private AppCompatImageView imageView;
public MySwitchPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public MySwitchPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MySwitchPreferenceCompat(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MySwitchPreferenceCompat(Context context) {
super(context);
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
imageView = holder.itemView.findViewById(R.id.preference_img);
setState(imageView);
}
@Override
protected void onClick() {
super.onClick();
setState(imageView);
}
private void setState(AppCompatImageView appCompatImageView) {
Drawable drawable = getContext().getResources().getDrawable(
getSharedPreferences().getBoolean(getKey(), false) ?
R.drawable.ic_arrow_upward_black_24dp : R.drawable.ic_arrow_downward_black_24dp);
appCompatImageView.setImageDrawable(drawable);
}
}
10、在自定义EditTextPreference时总感觉很奇怪,不知道为什么默认的EditTextPreference弹窗这么不好看(Google自己的规范上面都说Dialog内容到左右的边距为24dp):
默认的EditTextPreference
public class MyEditTextPreference extends EditTextPreference {
public MyEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public MyEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MyEditTextPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyEditTextPreference(Context context) {
super(context);
}
@Override
protected void onClick() {
View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_rename_layout, null);
final EditText editText = view.findViewById(R.id.rename_dialog_edit);
editText.setHint("");
editText.setText(getSharedPreferences().getString(getKey(), ""));
editText.setSelectAllOnFocus(true);
AlertDialog.Builder dialogBuild = new AlertDialog.Builder(getContext());
AlertDialog renameDialog = dialogBuild.setTitle(R.string.main_rename)
.setView(view)
.setTitle(getTitle())
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String string = editText.getText().toString().trim();
getSharedPreferences().edit().putString(getKey(), string).apply();
dialog.dismiss();
}
}).setNegativeButton(R.string.cancel, null).show();
}
}
11、如果有需求需要在MyEditTextPreference中拿到MainActivity的方法或变量,则需要在Fragment里面传进来,在这里用getContext拿到的Context是ContextThemeWrapper,MainActivity mainActivity = (MainActivity) getContext();这样强转是会报错的。
//Fragment中set进来
MyEditTextPreference editTextPreference = (MyEditTextPreference) findPreference("key_8");
editTextPreference.setActivity(getActivity());
12、修改Preference的样式:
<item name="colorAccent">@color/colorAccent</item>
<item name="android:textColorPrimary">#FFFF00FF</item>
<item name="android:textColorSecondary">#FF666666</item>
<item name="textColorAlertDialogListItem">#FF00FFFF</item>
13、自定义EditTextPreference在低版本手机中碰到过选中文字背景颜色不是你设置的颜色的情况:
<item name="android:textColorHighlight">#FF0000FF</item>