PreferenceFragmentCompat 设置界面

QuibblerQuibbler 2020-07-28 约 25 分钟 5650 次阅读

PreferenceFragmentCompat 设置界面

很早之就接触过PreferenceScreen设置项的常用用法(见《PreferenceScreen 设置项》),现在抽空再来了解一下它的承载主体PreferenceFragmentCompat及设置项的用法。

1、混乱的deprecate

先来屡一下Preference相关的包和类,到底哪些被deprecate?推荐用哪个?因为适配这个工作量不小,选错可能会给以后留下一个“惊喜”!

1.1、android.preference(退出历史)

最早Preference跟随系统库发布,位于Android源码android.preference包中,不需要添加额外依赖就可以直接使用。其中就有常用的Preference、PreferenceScreen、PreferenceActivity、PreferenceFragment等。但是这一锅全部被deprecate掉,不再推荐使用。

1.2、androidx.preference(推荐)

Google推出Jetpack,将一众混乱的各种support包、deprecate的包都统一起来。今天要说的PreferenceFragmentCompat就在androidx.preference包中。由于不是捆绑系统的类,所以使用PreferenceFragmentCompat需要在build.gradle中添加额外的依赖。

implementation 'androidx.preference:preference:1.1.1'

参考: Jetpack > androidx.preference">Android 开发者 > Jetpack > androidx.preference

1.3、PreferenceFragment

PreferenceFragment这个单独拿出来说一下,是因为在android.preference和androidx.preference包中都有,但是最终还是被deprecate掉,在代码的不断重构过程中最终还是被Google“抛弃”。与此类似的还有PreferenceDialogFragment、ListPreferenceDialogFragment、EditTextPreferenceDialogFragment等,都改为了_Compat兼容的类。

它的用法和后面要讲的PreferenceFragmentCompat差不多,继续往下看吧。

2、PreferenceFragmentCompat

继续《PreferenceScreen 设置项》写好xml设置页面,还需要借助PreferenceFragmentCompat,并且动态添加到Activity设置页面中。

2.1、继承PreferenceFragmentCompat

PreferenceFragmentCompat是抽象类,该类中只有一个抽象方法onCreatePreferences(Bundle savedInstanceState, String rootKey)

/**
     * 在{@link #onCreate(Bundle)}期间调用以提供此片段的首选项。希望子类直接或通过辅助方法(例如{@link 
     * #addPreferencesFromResource(int)})调用{@link #setPreferenceScreen(PreferenceScreen)}。 。
     *
     * @param savedInstanceState 如果要从先前保存的状态重新创建片段,则为该状态。
     * @param rootKey    如果不为null,则此首选项片段应使用此键植于{@link PreferenceScreen}。
     */
    public abstract void onCreatePreferences(Bundle savedInstanceState, String rootKey);

继承PreferenceFragmentCompat并实现该方法,相对于Activity的setContentView(R.layout.*)方法,调用setPreferencesFromResource(int preferencesResId,String key)加载res/xml目录下的设置页面。

public class SettingsFragment extends PreferenceFragmentCompat {
        @Override
        public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
            setPreferencesFromResource(R.xml.root_preferences, rootKey);
        }
    }

注意:可以使用PreferenceActivity似乎更加方便,不过整个页面只能是设置页,不利于页面的组合,使用Fragment会体会到它的强大便捷之处。android.preference.PreferenceActivity已经废弃,应当使用AndroidX包中的PreferenceActivity。

2.2、动态添加PreferenceFragmentCompat

PreferenceFragmentCompat也是Fragment,需要添加到Activity中。在Activity布局中放置一个FrameLayout,后面用来替换成设置Fragment。关于Fragment参考《Fragment用法》。

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

也可以不需要FrameLayout,直接替换

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

替换成设置Fragment

getSupportFragmentManager()    //获取FragmentManager
            .beginTransaction()    //开启一个事务
            .replace(R.id.settings, new SettingsFragment())
            .commit();             //提交Fragment

3、Preference回调

        Preference中定义了三种类型的回调接口,用来处理设置项的点击、选项值变化等场景。

对比区别联系执行顺序
onPreferenceClick通过preference.setOnPreferenceClickListener 和preference.setOnPreferenceChangeListener来注册listener使用的最先执行,返回true, 则不调用onPreferenceTreeClick方法2
onPreferenceChange通过preference.setOnPreferenceClickListener 和preference.setOnPreferenceChangeListener来注册listener使用的独立其他2个点击事件,总是会运行1
onPreferenceTreeClick是PreferenceActivity 中的一个方法,直接重写该该方法即可。返回true 则不执行默认动作或返回上层调用链。3

3.1、变化:Preference.OnPreferenceChangeListener

        当Preference的值将要发生变化的时候调用。通过控制返回值true/false是否接受本次值得变化。此时可以提示用户是否进行变更。

public boolean onPreferenceChange(Preference preference, Object newValue) {
                //做业务逻辑处理
                boolean result = (boolean) newValue;
                
                if (result) {
                    // 返回true  本次值变化生效
                    return true;
                } else {
                    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
                    builder.setTitle("选择")
                            .setMessage("是否确定关闭?")
                            .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    //用户选择取消,不关闭。直接关闭Dialog即可,因为后面已经返回了false,本次修改不造成变化
                                    dialog.dismiss();
                                }
                            })
                            .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    //用户选择确定,那么本次值虽然后面已经返回false,不再接受,但是这里可以继续根据用户的选择更改。
                                }
                            })
                            .create()
                            .show();
                    // 返回false 本次变化失效。在Dialog中通过选择控制值。
                    return false;
                }
            }

Preference设置变化监听器setOnPreferenceChangeListener

preference.setOnPreferenceChangeListener(...);

3.2、点击:Preference.OnPreferenceClickListener

点击Preference界面设置项的时候调用,实际验证:当值发生变化后,才调用该函数。如果这个时候再去做点击判断是否改变值就晚了。

场景:用户点击一个设置项,弹出对话框告诉用户是否确认修改,如果取消那么值不变。就可以给指定的Preference设置点击事件;或者先拦截,用户进行选择值再变化。

顺序:先调用onPreferenceChange函数,然后才设置checkbox的状态,最后才会调用onPreferenceClick。

private Preference.OnPreferenceClickListener preferenceClickListener = new Preference.OnPreferenceClickListener() {
        @Override
        public boolean onPreferenceClick(Preference preference) {
            return false;
        }
    };

Preference设置点击监听器setOnPreferenceChangeListener

preference.setOnPreferenceClickListener(preferenceClickListener);

3.3、跳转Activity

一般点击设置页面,有些是实现跳转。可以在标签中添加<intent/>实现跳转:

<Preference
        app:icon="@drawable/setting_icon_1"
        app:key="jump"
        app:summary="跳转到另一个Activity"
        app:title="跳转">
        
        <intent android:action="com.quibbler.jump" />
        
    </Preference>

或者更精确的添加android:targetPackage和android:targetClass指定跳转的Activity,防治系统中有其它应用有相同的Action冲突:

<intent android:action="com.quibbler.jump"
        android:targetPackage="com.easyicon.server"
        android:targetClass="com.easyicon.server.activity.JumpHereActivity"/

另一种方法,在继承的PreferenceFragmentCompat类中,通过key找到设置项,添加点击事件。

Preference preference = getPreferenceManager().findPreference("jump");
    preference.setOnPreferenceClickListener(preferenceClickListener);

Preference.OnPreferenceClickListener设置项点击监听器,点击进行跳转。关于Activity跳转,参考启动Activity的几种方式。

private Preference.OnPreferenceClickListener preferenceClickListener = new Preference.OnPreferenceClickListener() {
            @Override
            public boolean onPreferenceClick(Preference preference) {
                String key = preference.getKey();
                if ("jump".equals(key)) {
//                    Intent intent = new Intent();
//                    intent.setAction("com.quibbler.jump");
//                    getActivity().startActivity(intent);
                }
                return false;
            }
        };

坑点:如果这里onPreferenceClick(Preference preference)方法返回了true,那么在xml设置布局中<Preference/>节点下设置的<intent/>将不起作用。

4、PreferenceManager指定保存文件名

默认保存在/data/data/包名/shared_prefs/包名_preference.xml中,可以通过PreferenceManager指定保存设置Preference的文件名

PreferenceManager preferenceManager = getPreferenceManager();

指定保存SharedPreference文件名:

preferenceManager.setSharedPreferencesName(name);
    preferenceManager.setSharedPreferencesMode(Context.MODE_PRIVATE);

5、动态删除/添加Perference

有时候设置项需要根据不同的条件变化,可以加载不同的xml/设置布局,也可以在代码中动态的添加删除Preference设置项。所以必须要有PreferenceFragmentCompat对象

PreferenceFragmentCompat mSettingsFragment = new SettingsFragment();

5.1、动态删除

首先需要找到要删除的Preference,借助PreferenceFragmentCompat对象获取到PreferenceManager设置管理器

PreferenceManager preferenceManager = mSettingsFragment.getPreferenceManager();

借助PreferenceManager设置管理器,通过key找到Preference设置项:

Preference preference = preferenceManager.findPreference("key_set");

 ①借助PreferenceScreen删除

要从哪删除?当然是PreferenceFragmentCompat对象中设置容器中,我们设置XML布局中的顶级容器标签就是PreferenceScreen。先获取PreferenceFragmentCompat的容器

PreferenceScreen preferenceScreen = mSettingsFragment.getPreferenceScreen();

再从中删除即可:

PreferenceScreen preferenceScreen = mSettingsFragment.getPreferenceScreen();
    preferenceScreen.removePreference(preference);

 ②借助PreferenceCategory删除

还可以单独从某个设置组中删除,先通过PreferenceManager设置管理器找到设置组。从该设置组中删除Preference设置项。

PreferenceCategory preferenceCategory = preferenceManager.findPreference("key_category");
    if (null != preferenceCategory) {
        //从组别中删除该设置项,最好判空
        preferenceCategory.removePreference(preferenceScreen);
    }

5.2、动态添加

添加,构造好一个设置项,初始化各种参数title、key等。

Preference preference = new Preference(this);
    preference.setTitle("添加新的设置");
    preference.setKey("pref_new_key");

直接添加到PreferenceScreen中

PreferenceScreen preferenceScreen = mSettingsFragment.getPreferenceScreen();
    preferenceScreen.addPreference(preference);

添加到指定设置组中:

PreferenceCategory preferenceCategory = preferenceManager.findPreference("key_category");
    //添加到该组的最后一个位置上
    preferenceCategory.addPreference(preference);

注意:无论如何添加设置项都是添加到最后一个位置,或者组的最后一个位置。这就不太好真正实现设置项的隐藏和显示。源码设置Preference的插入似乎有突破口,根据Index插入。

推荐博客:

        Google > Docs > Reference > PreferenceFragmentCompat

        PreferenceFragmentCompat

        Android Preference详解之初识Preference及Preference系

        PreferenceFragment去完成设置页面

        Android开发之PreferenceActivity的使用

        

相关推荐

精选
ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager
Android

ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager

ViewPager1、ViewPagerandroidx.viewpager.widget.ViewPager,Android中使用非常广泛的控件,可以说是APP必备:首次打开引导页、页面Banner广告等。常用方法:setAdapter() 设置适配器setOffscreenPageLimit() 设置缓存的页面个数,默认是 1setCurrentItem() 跳转到特定的页面setOnPage

1.1k
获取Android内置WebView内核版本
Android

获取Android内置WebView内核版本

获取Android内置WebView内核版本竟然能遇到这样奇葩的事情,网页用的技术过于新颖,以至于只支持高版本Chromium内核的,低版本安卓系统中内置的内核版本较低,无法加载前端页面。1、设置查看在系统设置里 > 应用 > 应用管理 > 显示系统应用,查看WebView组件:2、页面查看通过WebView发起的网络请求,都会带上浏览器的UA,通常页面都可以通过UA判断浏览器的内核版本。这里有两

1.1w
Android 14适配总结
Android

Android 14适配总结

Android 14适配总结毕业工作至今已经适配了三个Android大版本,从Android 11到Android 12、再到Android 13。2023年,Google即将推出的Android 14,上半年已经开始第一批适配。现在,第四个Android版本已经适配完,总结记录一下。1、Android 14计划Google一般会在2月份对外发布预告,同时放出开发者预览版。“拉通”各大平台、厂商以

1.1w

评论 (1)

评论功能已关闭,仅展示历史评论
Quibbler
Quibbler


        自定义Preference界面,重写onBindViewHolder(PreferenceViewHolder holder)方法加载自定义Layout布局

    /**
     * Binds the created View to the data for this Preference.
     * 
     * This is a good place to grab references to custom Views in the layout and
     * set properties on them.
     * 
     * Make sure to call through to the superclass's implementation.
     *
     * @param holder The ViewHolder that provides references to the views to fill in. These views
     *               will be recycled, so you should not hold a reference to them after this method
     *               returns.
     */
    public void onBindViewHolder(PreferenceViewHolder holder) {
        ...
    }