Android 关于Preference相关类的分析

系统里设置Settings app 里面是用Preference来做的,这些在其他app里也有涉及,比如dialer的设置部分,关于Preference这里涉及到一些类,以后会常用碰到的,做一个笔记记录和分析一下。

  • ActivityPreferenceActivity
  • FragmentPreferenceFragment
  • PreferencePreference 多个子类
    SwitchPreference,RadioButtonPreference,SeekBarPreference,DialogPreference,RingtonePreference,
    以及多个DialogPreference子类ListPreference,MultiCheckPreference,EditTextPreference

Preference
Preference这个类内部包含的成员变量有:

private Context mContext;
private PreferenceManager mPreferenceManager;
private PreferenceDataStore mPreferenceDataStore;
private long mId;
private OnPreferenceChangeListener mOnChangeListener;
private OnPreferenceClickListener mOnClickListener;
private CharSequence mTitle;
private int mTitleRes;
private CharSequence mSummary;
private int mIconResId;
private Drawable mIcon;
private Intent mIntent;
private String mFragment;
private Bundle mExtras;
private boolean mEnabled = true;
private boolean mSelectable = true
private OnPreferenceChangeInternalListener mListener;
private PreferenceGroup mParentGroup;

这些属性都是在平时会用到了,一个标准的Preference最简单就只需要一个标题mTitle

内部接口

  public interface OnPreferenceChangeListener {
     boolean onPreferenceChange(Preference preference, Object newValue);
  }
  public interface OnPreferenceClickListener {
     boolean onPreferenceClick(Preference preference);
  }

分别在Preference被点击和改变值的时候回调。

Preference在初始化的时候会默认加载系统layout,然后加载对应属性。
成员方法

protected View onCreateView(ViewGroup parent)
protected void onBindView(View view) 


数据存储相关方法

public Context getContext()
public SharedPreferences getSharedPreferences()
public SharedPreferences.Editor getEditor()
protected void notifyChanged()
public PreferenceManager getPreferenceManager()
protected boolean persistString(String value)
protected boolean persistInt(int value)
protected boolean persistFloat(float value)
protected boolean persistLong(long value)

存储数据与普通Sharedpreference类似;

protected boolean persistBoolean(boolean value) {
    if (!shouldPersist()) {
        return false;
    }
    if (value == getPersistedBoolean(!value)) {
        return true;
    }
    PreferenceDataStore dataStore = getPreferenceDataStore();
    if (dataStore != null) {
        dataStore.putBoolean(mKey, value);
    } else {
        SharedPreferences.Editor editor = mPreferenceManager.getEditor();
        editor.putBoolean(mKey, value);
        tryCommit(editor);
    }
    return true;
}
protected boolean getPersistedBoolean(boolean defaultReturnValue) {
    if (!shouldPersist()) {
        return defaultReturnValue;
    }
    PreferenceDataStore dataStore = getPreferenceDataStore();
    if (dataStore != null) {
        return dataStore.getBoolean(mKey, defaultReturnValue);
    }
    return mPreferenceManager.getSharedPreferences().getBoolean(mKey, defaultReturnValue);
}

以上就是Preference里比较有用的成员和方法;其余的子类则是分别通过继承和重载来改写view和数据部分,以达到实现不同的功能。


下面以DialogPreference为例来分析它是如何实现点击Preference就弹出Dialog对话框的。
一进入DialogPreference就看到了一些和AlertDialog相关的成员。

    private AlertDialog.Builder mBuilder;
    private CharSequence mDialogTitle;
    private CharSequence mDialogMessage;
    private Drawable mDialogIcon;
    private CharSequence mPositiveButtonText;
    private CharSequence mNegativeButtonText;
    private int mDialogLayoutResId;
    private Dialog mDialog;

其余大部分成员方法都是用来设置和Dialog相关的方法,比如设置标题,设置内容等。
然后

    @Override
    protected void onClick() {
        if (mDialog != null && mDialog.isShowing()) return;
        showDialog(null);
    }

最重要的是重载了onClick,在点击的时候弹出对话框。
由于DialogPreference是抽象类,所以在对话框关闭的方法里留了空

    protected void onDialogClosed(boolean positiveResult) {
    	//等待子类继承
    }

一般子类继承这个方法都是用来更新数据和重绘view。

在xml里的属性用法举例,

<PreferenceScreen   //Preference集合,类似于Viewgroup
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
    android:title="@string/network_dashboard_title">
    
    <Preference
        android:key="mobile_network_settings"    		//key
        android:title="@string/network_settings_title"  //title
        android:summary="@string/summary_placeholder"	//summary
        android:icon="@drawable/ic_network_cell"		//icon
        android:persistent="false"						//是否可持久化存储
        android:dependency="toggle_airplane"	
        android:entries="@array/tty_mode_entries"		//列表的names
        android:entryValues="@array/tty_mode_values"	//列表的values
        android:defaultValue="true"						//默认值
        android:fragment="com.android.settings.TetherSettings"	//点击后跳转的fragment
        android:order="-15"
        settings:keywords="@string/keywords_more_mobile_networks"
        settings:userRestriction="no_config_mobile_networks"
        settings:useAdminDisabledSummary="true">
        <intent
            android:action="android.intent.action.MAIN"
            android:targetPackage="com.android.phone"
            android:targetClass="com.android.phone.MobileNetworkSettings"/>
    </Preference>
</PreferenceScreen>

PreferenceActivity

PreferenceActivity是继承自ListActivity的,
ListActivity里面维持了一个ListView,
这里面的Listview是设置了内部成员监听的。

private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() {
    public void onItemClick(AdapterView<?> parent, View v, int position, long id)
    {
        onListItemClick((ListView)parent, v, position, id);
    }
};
    
mList.setOnItemClickListener(mOnClickListener);

所以PreferenceActivity的界面里,即使不手动设置点击监听,也是会有回调的,PreferenceActivity只需要继承onListItemClick这个方法就行。

PreferenceActivity是个抽象类,在它的Oncreate里有许多默认设置,
其中关键点是构建Header,头部条目。

   if (!onIsHidingHeaders()) {
       onBuildHeaders(mHeaders);
   }
   public void onBuildHeaders(List<Header> target) {
       // Should be overloaded by subclasses
   }

数据部分需要子类去重写,如果子类不重写则PreferenceActivity就显示为空。

重要构造方法

public void loadHeadersFromResource(@XmlRes int resid, List<Header> target)
public void addPreferencesFromResource(int preferencesResId)

PreferenceActivity经典使用举例
在Dialer的setting界面,就是DialerSettingsActivity,继承自PreferenceActivity;

@Override
  public void onBuildHeaders(List<Header> target) {
	...
    Header soundSettingsHeader = new Header();
    soundSettingsHeader.titleRes = R.string.sounds_and_vibration_title;
    soundSettingsHeader.fragment = SoundSettingsFragment.class.getName();
    soundSettingsHeader.id = R.id.settings_header_sounds_and_vibration;
    target.add(soundSettingsHeader);
    ...
}

比较明显的就是,在这个界面里看到的内容条目都是一个个Header,然后构成列表出现。
当然这些都是用代码方式填充数据的。

另一个继承的PreferenceActivityCallFeaturesSetting,则是用addPreferencesFromResource

   @Override
   protected void onResume() {
       super.onResume();
       ...
       addPreferencesFromResource(R.xml.call_feature_setting);
       ...
   }

这两个的区别就在于 一个是从xml,一个是从java代码里添加数据。
onBuildHeaders需要继承,addPreferencesFromResource只需要在Oncreate里调用。

忘了说,这里的HeaderPreferenceActivity的内部类,属性部分几乎和Preference大致相同。
不过和Preference的区别在于,Preference自带view,而这里的Header更纯粹的属于一个item。

以上就是PreferenceActivity相关部分,使用起来的特点就是这几个方法,获取数据部分,以及点击回调,其余的就是点击事件处理了。


PreferenceFragment
PreferenceFragment在平时使用中更频繁,比如在Setting里,所有的界面都是一个空Activity靠加载不同的Fragment去调度。

按照生命周期来说与普通的Fragment没差别。

public void onCreate
public View onCreateView
public void onViewCreated
public void onActivityCreated
public void addPreferencesFromIntent(Intent intent)
public void addPreferencesFromResource(@XmlRes int preferencesResId)
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)
public Preference findPreference(CharSequence key)
private void bindPreferences()

所以使用起来也不会很麻烦。

接下来说说Setting中的那些PreferenceFragment
在Setting里,似乎所有的设置子项都是用的PreferenceFragment
比如

  • 设置网络的NetworkDashboardFragment
  • 连接设备的ConnectedDeviceDashboardFragment
  • 应用通知的AppAndNotificationDashboardFragment
  • 电池管理的PowerUsageSummary
  • 显示管理的DisplaySettings
  • 声音管理的SoundSettings
  • 存储管理的StorageDashboardFragment
  • 安全设置的SecuritySettings
  • 系统设置的SystemDashboardFragment

他们全都是PreferenceFragment的子类。

DashboardFragment extends SettingsPreferenceFragment
SettingsPreferenceFragment extends InstrumentedPreferenceFragment
InstrumentedPreferenceFragment extends ObservablePreferenceFragment
ObservablePreferenceFragment extends PreferenceFragment

NetworkDashboardFragment为例看看是怎么做的。

    @Override
    protected int getPreferenceScreenResId() {
        return R.xml.network_and_internet;
    }
        @Override
    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
        return buildPreferenceControllers(context, getLifecycle(), mMetricsFeatureProvider, this
                /* fragment */,
                this /* mobilePlanHost */);
    }
    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
            Lifecycle lifecycle, MetricsFeatureProvider metricsFeatureProvider, Fragment fragment,
            MobilePlanPreferenceHost mobilePlanHost) {
            ....
    }

这里只重载了两个方法

  1. getPreferenceScreenResId获取xml的id
  2. getPreferenceControllers得到所有找到的preference的控制器

在父类NetworkDashboardFragment里,AbstractPreferenceController是用来替换控制的,
比如

    @Override
    public boolean onPreferenceTreeClick(Preference preference) {
        Collection<AbstractPreferenceController> controllers = mPreferenceControllers.values();
        // If preference contains intent, log it before handling.
        mMetricsFeatureProvider.logDashboardStartIntent(
                getContext(), preference.getIntent(), getMetricsCategory());
        // Give all controllers a chance to handle click.
        for (AbstractPreferenceController controller : controllers) {
            if (controller.handlePreferenceTreeClick(preference)) {
                return true;
            }
        }
        return super.onPreferenceTreeClick(preference);
    }

点击的时候用对应的controller去实现点击。

      Collection<AbstractPreferenceController> controllers = mPreferenceControllers.values();
      for (AbstractPreferenceController controller : controllers) {
          controller.displayPreference(screen);
      }

显示的时候用对应的controller去显示。

这样做的好处就是解耦,让代码逻辑更清晰,方便拓展。
因为设置里的设置项确实非常多,应该各自写各自的,互相影响才小。


好了,基本上该写的都写完了,如果我们要自己写Preference界面,可以用PreferenceActivity或者PreferenceFragment都行,他俩都是抽象类,需要子类继承,在使用的时候直接写个xml来使用,设置项也可以单独去用不同的Preference控制。

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android的Preference是一种轻量级的数据存储机制,可以存储简单的键值对应关系。其中的键值对可以是boolean、float、int、long和String。Preference适用于存储应用程序的配置信息和一些简单的状态信息。 在Android中,通过SharedPreferences来访问和操作Preference。SharedPreferences提供了四个方法:putBoolean()、getBoolean()、putInt()、getInt()、putFloat()、getFloat()、putLong()、getLong()、putString()和getString()。使用这些方法可以对Preference进行读写操作。 具体步骤如下: 1. 获取SharedPreferences对象 SharedPreferences preferences = getSharedPreferences("name", Context.MODE_PRIVATE); 其中,name表示Preference的名称,MODE_PRIVATE表示该文件只能被当前应用程序读写。 2. 写入数据 editor = preferences.edit(); editor.putBoolean("key", true); editor.apply(); 3. 读取数据 boolean value = preferences.getBoolean("key", false); 其中,"key"表示写入时所使用的键值,false表示默认值。 需要注意的是,Preference适用于存储简单的数据,如果需要存储大量的数据或者复杂的数据结构,建议使用SQLite数据库或者其他高级的数据存储机制。 除此之外,Preference还可以支持PreferenceScreen的层级结构,可以在同一个PreferenceActivity中显示多个Preference。通过调用getPreferenceScreen()方法获取PreferenceScreen对象,可以设置和修改PreferenceScreen的属性。 总的来说,Preference是一个轻便、易用的数据存储机制,适用于简单的数据存储需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值