用Androidx的方式使用Preference
起因
- 可能是因为targetApi=29的原因,在使用传统的PreferenceActivity的时候AS提醒我里面的一些方法已经被弃用,作为一个非常激进的业余菜鸟开发者,我的强迫症促使我去寻找其解决方案
- 近期收到我的小软件AndroCode有些用户反馈设置的第二级打不开,点击没有反应
找轮子
我开始百度找轮子,得知android10使用androidx全面替代,看了一下迁移的对应关系migrate,于是又百度androidx.preference,但结果寥寥无几,只有androidx PreferenceDialogFragmentCompat 和 DialogPreference的配合应用这一篇有一些记载,但对于我们这样的菜鸟很不友好,没有讲基础的使用方法或贴代码,里面的参考文档还被墙打不开。
不得已去看官方API,终于找到了使用方法指南,竟然有中文!!!还这么简洁,我大喜过望,赶紧上手操作
导入依赖
implementation 'androidx.preference:preference:1.1.0'
implementation 'androidx.core:core:1.2.0-beta01'
implementation 'androidx.fragment:fragment:1.2.0-rc01'
官方Demo
<PreferenceScreen
xmlns:app="http://schemas.android.com/apk/res-auto">
<SwitchPreferenceCompat
app:key="notifications"
app:title="Enable message notifications"/>
<Preference
app:key="feedback"
app:title="Send feedback"
app:summary="Report technical issues or suggest new features"/>
</PreferenceScreen>
public class MySettingsFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.preferences, rootKey);
}
}
public class MySettingsActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.settings_container, new MySettingsFragment())
.commit();
}
}
首先是没有了header的方式,但我的APP暂时还是需要这种布局,于是用RecyclerView模拟了一个
activity_settings_item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:paddingStart="15dp"
android:paddingTop="10dp"
android:paddingEnd="15dp"
android:paddingBottom="10dp">
<!--actionBarItemBackground -->
<TextView
android:id="@+id/activity_settings_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAllCaps="true"
android:textStyle="bold"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
<TextView
android:id="@+id/activity_settings_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
</LinearLayout>
activity_settings.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:id="@+id/activity_settings_layout"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/activity_settings_recyclerv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<FrameLayout
android:id="@+id/activity_settings_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
这里偷了个懒,用显示隐藏RecycerView的方式来给Fragment腾地方
由于PreferenceScreen还要用Fragment显示, 于是在AppCompatActivity中用recyclerview来触发事件显示fragment
onCreate:
headers.add(new SettingHeader("应用", "配置应用主题、会话", new SettingFragment(R.xml.settings_app)));
headers.add(new SettingHeader("编辑器", "配置编辑器、代码、保存", new SettingFragment(R.xml.settings_editor)));
headers.add(new SettingHeader("构建、运行", "配置工程构建和运行设置", new SettingFragment(R.xml.settings_build)));
headers.add(new SettingHeader("关于", "介绍等", new SettingFragment(R.xml.settings_about)));
recyclerv.setAdapter(new BaseRecyclerAdapter<SettingHeader>(headers) {
@Override
protected int getItemLayoutId(int viewType) {
return R.layout.activity_settings_item;
}
@Override
protected void bindData(@NonNull RecyclerViewHolder holder, int position, SettingHeader item) {
holder.getTextView(R.id.activity_settings_label).setText(item.label);
holder.getTextView(R.id.activity_settings_message).setText(item.message);
holder.itemView.setOnClickListener(v -> {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.activity_settings_container, item.fragmentCompat)
.commit();
recyclerv.setVisibility(View.GONE);
});
}
});
recyclerv.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, false));
为了减少代码,我机智地复用一个SettingFragment来加载不同xml
public static class SettingHeader {
public String label;
public String message;
public PreferenceFragmentCompat fragmentCompat;
public SettingHeader(String label, String message, PreferenceFragmentCompat fragmentCompat) {
this.label = label;
this.message = message;
this.fragmentCompat = fragmentCompat;
}
}
public static class SettingFragment extends PreferenceFragmentCompat {
private final int id;
public SettingFragment(int xmlId) {
super();
this.id = xmlId;
}
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(id, rootKey);
}
@Override
public void onDisplayPreferenceDialog(Preference preference) {
if (preference instanceof GenKeystorePreference) {
((GenKeystorePreference) preference).showDialog();
} else
super.onDisplayPreferenceDialog(preference);
}
}
于是我满怀信心地用AS跑了起来,一起看起来是那么地完美
没问题,打开应用item
???这是什么鬼
寻找错误原因
因为用到了一个换肤框架,有些兼容问题,起初我以为是字体颜色和背景一样,于是开始捣鼓主题,禁用框架,但毫无结果
终于,机缘巧合之下,log提示我自定义的Preference有问题,DialogPreference不再提供默认的新建Dialog操作,要通过onDisplayPreferenceDialog来设置,将其修复后log提示我没有Preference have no Key!
但我明明有定义,完全是按照官方的教程来的啊!思考半天,我尝试用android:替换app:,最后运行如下
官方文档有错!本人记下了!!!
看这架势应该是支持icon,预留了一个ImageView
结论
官方文档有错,还是用android:而不是app:,估计是升级lib忘了同步还是写错了?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。