·PreferenceActivity可以显示一系列Header,每一个Header可以关联一个Fragment或者Activity。此外,它还可以直接显示Preference条目。

·PreferenceActivity显示Header的时候有两种模式:single panetwo panes;如果是Fragment,那么在two panes模式下,也就是大屏模式下,它可以同时显示HeaderFragment,这充分利用了屏幕的空间。而在singlepane模式下只会显示Header,无论如何,我们都可以在Header关联的Fragment中再显示Preference条目。

如上描述可用示意图标示:


在介绍它的使用方法之前,为了更好的理解PreferenceActivity,我会先对源码做一个简单的分析,分析结束后再介绍它的用法,包括显示Header和显示preference,这样更容易理解为什么会这么使用。

这里的Preference指的是显示在PreferenceActivity中的UI构建块,例如ListPreference,CheckBoxPreference等,他们都是Preference的子类(非直接):


一.源码分析

这里主要介绍分析PreferenceActivity中的Header,onBuildHeaders,加载Preference,加载Header,事件处理等内容,下图是简单概要:


1.继承关系:


可以看到PreferenceActivity继承自ListActivity,ListActivity是一个封装了ListViewActivity,ListActivity中给ListView设置了事件监听器:

mList.setOnItemClickListener(mOnClickListener);


这个事件监听器是这样的:

private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() {

public void onItemClick(AdapterView<?> parent, View v, int position, long id)

{

onListItemClick((ListView)parent, v, position, id);

}

};


可以看到在监听器中,又简单调用了onListItemClick方法,这个方法的定义如下:

protected void onListItemClick(ListView l, View v, int position, long id) {

}

它是一个空的方法,如果你的Activity继承自ListActivity,想处理ListView的事件的话,只需要重写这个方法。

ListActivity中提供了给ListView设置适配器的接口,但是适配器还得自己去写,因此可以说ListActivity功能有限。PreferenceActivity继承了ListActivity,那么它当然主要是一个ListView了,那么它会怎样给ListView设置适配器呢?它又怎样处理按键事件?

2.Header

HeaderPreferenceActivity一个非常重要的概念,PreferenceActivity可以显示一系列的Header,每一个Header可以给它关联一个fragment,这样当你点击这个Header时,就会打开它关联的fragment,但是不局限于此,从源码来看,如果没有关联Fragment,那么也可以设置Intent,此时,可以通过Intent打开对应的Activity

这个类还有两种模式,singlepanetwo panes,分别针对小屏幕设备和大屏幕设备。对小屏幕设备而言,屏幕只会显示header,而大屏则会同时显示它关联的frament

Header是一个定义在PreferenceActivity中的内部类:

public static final class Header implements Parcelable {

它实现了Parcelable接口,表明它可以被序列化。它无非就是一个容器,里面保存了一个条目的所有相关的内容,比如这个条目的title-标题,summary-描述,fragment-关联的fragment等等。当我们要显示它的时候,我们只需要构造好Header,然后把它提交给PreferenceActivity就可以了,提交的方法是重写onBuildHeaders方法,这个方法的参数是一个List<Header >,只需要把自己构造的Header添加到这个List就可以了。此外,还需要覆写isValidFragment方法,后面会有介绍。

3.给ListView设置适配器

setListAdapter(new HeaderAdapter(this, mHeaders, mPreferenceHeaderItemResId,

mPreferenceHeaderRemoveEmptyIcon));


可以看到它给ListView设置的适配器是一个HeaderAdapter.那么这个Adapter是什么呢?

private static class HeaderAdapter extends ArrayAdapter<Header> {

private static class HeaderViewHolder {

ImageView icon;

TextView title;

TextView summary;

}

private LayoutInflater mInflater;

private int mLayoutResId;

private boolean mRemoveIconIfEmpty;

public HeaderAdapter(Context context, List<Header> objects, int layoutResId,

boolean removeIconBehavior) {

super(context, 0, objects);

mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

mLayoutResId = layoutResId;

mRemoveIconIfEmpty = removeIconBehavior;

}

@Override

public View getView(int position, View convertView, ViewGroup parent) {

HeaderViewHolder holder;

View view;

if (convertView == null) {

view = mInflater.inflate(mLayoutResId, parent, false);

holder = new HeaderViewHolder();

holder.icon = (ImageView) view.findViewById(com.android.internal.R.id.icon);

holder.title = (TextView) view.findViewById(com.android.internal.R.id.title);

holder.summary = (TextView) view.findViewById(com.android.internal.R.id.summary);

view.setTag(holder);

} else {

view = convertView;

holder = (HeaderViewHolder) view.getTag();

}

// All view fields must be updated every time, because the view may be recycled

Header header = getItem(position);

if (mRemoveIconIfEmpty) {

if (header.iconRes == 0) {

holder.icon.setVisibility(View.GONE);

} else {

holder.icon.setVisibility(View.VISIBLE);

holder.icon.setImageResource(header.iconRes);

}

} else {

holder.icon.setImageResource(header.iconRes);

}

holder.title.setText(header.getTitle(getContext().getResources()));

CharSequence summary = header.getSummary(getContext().getResources());

if (!TextUtils.isEmpty(summary)) {

holder.summary.setVisibility(View.VISIBLE);

holder.summary.setText(summary);

} else {

holder.summary.setVisibility(View.GONE);

}

return view;

}

}

从定义中可以看到,HeaderAdapter是一个ArrayAdapter的子类,并且复写了getView方法。在getView方法中,根据header中的内容,填充icon,title.summary等。

4.PreferenceActivity的事件处理

@Override

protected void onListItemClick(ListView l, View v, int position, long id) {

if (!isResumed()) {

return;

}

super.onListItemClick(l, v, position, id);

if (mAdapter != null) {

Object item = mAdapter.getItem(position);

if (item instanceof Header) onHeaderClick((Header) item, position);

}

}

可以看到它重写了onListItemClick方法,用于处理事件,在这个方法中调用了onHeaderClick方法:

public void onHeaderClick(Header header, int position) {

if (header.fragment != null) {

if (mSinglePane) {

int titleRes = header.breadCrumbTitleRes;

int shortTitleRes = header.breadCrumbShortTitleRes;

if (titleRes == 0) {

titleRes = header.titleRes;

shortTitleRes = 0;

}

startWithFragment(header.fragment, header.fragmentArguments, null, 0,

titleRes, shortTitleRes);

} else {

switchToHeader(header);

}

} else if (header.intent != null) {

startActivity(header.intent);

}

}


这个方法中判断header中是否设置了frament,如果设置了,又会判断是不是处于singpane模式,如果是,就会启动fragment,如果不是singlepane,那么肯定就是two panes模式了,这个时候则切换到Header关联的Fragment中显示Fragment中的preference。如果没有设置fragment,那么又会判断有没有设置Intent,如果设置了Intent,那么会启动Intent所指向的Activity。

关于Header的显示,我们可以自定义适配器,这时候需要重写setListAdapter方法,比如:

@Override

public void setListAdapter(ListAdapter adapter) {

if (adapter == null) {

super.setListAdapter(null);

} else {

}

}

这个方法是ListActivity中提供的用于设置适配器的方法,这个方法在PreferenceActivity的onCreate方法中调用,用来给ListView设置适配器。
构造Header则需要重写onBuildHeaders方法,比如:

@Override

public void onBuildHeaders(List<Header> headers) {

}

这个方法在PreferenceActivity的onCreate方法中调用。它的调用在setListAdapter之前,用来构造Header。

二.简单的使用举例:

1.代码构造Header

写一个Activity,继承PreferenceActivity,然后覆写onBuildHeaders方法即可:

public class MainActivity extends PreferenceActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

//setContentView(R.layout.activity_main);

}

@Override

public void onBuildHeaders(List<Header> target) {

for(int i=0;i<5;i++){

Header header = new Header();

header.title = "hello"+i;

header.summary = "hehe"+i;

header.extras = new Bundle();

header.fragment = MyFrament.class.getName();

target.add(header);

}

super.onBuildHeaders(target);

}

}


注意:

1.setContentView要注释掉,因为如果重新设置ContentView,那么ListActivity中设置的ListView就找不到了。

2.onBuildHeaders中,构造的header要添加到target列表中。

3.fragment一定要设置,不设置就不会显示的,因此你需要自己实现一个与Header关联的Fragment,这个Fragment会在点击Header的时候被打开。

4.因为PreferenceActivity中已经提供了默认的适配器,所以,如果不需要自定义,则不需重写setListAdapter方法。

这样只是单纯的可以显示一些Header,但是点击Header还是会报错,我们还必须要覆写一个方法:

@Override

protected boolean isValidFragment(String fragmentName) {

return true;

}

即明确告诉PrefercenceActivity我们准备了一个合法的Fragment

Fragment中可以显示preference了,这里暂时什么都不显示,这样打开的是一个空页面:

public class MyFrament extends PreferenceFragment {

public void onCreate(Bundle b) {

super.onCreate(b);

}

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

return null;

}

}

代码效果如下:


2.xml构造Header

XML构造Header更加简单方便。

2.1构建xml文件:

<preference-headers

xmlns:android="http://schemas.android.com/apk/res/android">

<header android:fragment="com.konka.preferenceactivitytest1.MyFrament"

android:icon="@mipmap/ic_launcher"

android:title="Prefs 1"

android:summary="An example of some preferences." />

<header android:fragment="com.konka.preferenceactivitytest1.MyFrament"

android:icon="@mipmap/ic_launcher"

android:title="Prefs 2"

android:summary="Some other preferences you can see.">

<!-- Arbitrary key/value pairs can be included with a header as

arguments to its fragment. -->

<extra android:name="someKey" android:value="someHeaderValue" />

</header>

<header android:icon="@mipmap/ic_launcher"

android:title="Intent"

android:summary="Launches an Intent.">

<intent android:action="android.intent.action.VIEW"

android:data="http://www.baidu.com" />

</header>

</preference-headers>

2.2加载XML文件:

public void onBuildHeaders(List<Header> target) {

// for(int i=0;i<5;i++){

// Header header = new Header();

// header.title = "hello"+i;

// header.summary = "hehe"+i;

// header.fragment = MyFrament.class.getName();

// target.add(header);

// }

// super.onBuildHeaders(target);

loadHeadersFromResource(R.xml.preference_header, target);

}


也就是说还是要重写onBuilderHeaders方法,只不过这次试用loadHeadersFromResource方法从xml中加载header了。
代码效果如下:

3.PreferenceActivity显示preference

PreferenceActivity除了可以显示Header和它关联的fragment之外,还可以直接显示preference,这些prefercence可以直接从XML文件中加载。PreferenceActivity显示preferenceApi大都可以在PreferenceFragment中找到,而且更推荐使用PreferenceFragment来显示preference,不过在很多比较旧的的代码中,还保留着很多使用PreferenceActivity显示preference的代码。

使用PreferenceActivity显示preference只需要两步:

3.1xml文件下创建xml配置文件:

<?xml version="1.0" encoding="utf-8"?>

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >

<Preference

android:icon="@drawable/ic_settings_wifi_4"

android:key="network"

android:title="@string/connectivity_network" >

<intent

android:targetClass="com.android.tv.settings.connectivity.NetworkActivity"

android:targetPackage="com.android.tv.settings" />

</Preference>

<Preference

android:icon="@drawable/ic_settings_cast"

android:key="cast"

android:title="@string/system_cast" >

<intent android:action="com.google.android.settings.CAST_RECEIVER_SETTINGS" />

</Preference>

<Preference

android:icon="@drawable/ic_settings_apps"

android:key="apps"

android:title="@string/device_apps" >

<intent

android:targetClass="com.android.tv.settings.device.apps.AppsActivity"

android:targetPackage="com.android.tv.settings" />

</Preference>

<Preference

android:icon="@drawable/ic_settings_storage"

android:key="storagereset"

android:title="@string/device_storage_reset" >

<intent

android:targetClass="com.android.tv.settings.device.StorageResetActivity"

android:targetPackage="com.android.tv.settings" />

</Preference>

<Preference

android:icon="@drawable/ic_settings_about"

android:key="about_device"

android:title="@string/about_preference">

<intent

android:targetClass="com.android.tv.settings.about.AboutActivity"

android:targetPackage="com.android.tv.settings" />

</Preference>

</PreferenceScreen>


PreferenceScreen中的内容会显示在一个新的屏幕中,PreferenceCategory标示一个分组,它会成为一组条目的头目,而且它是不能获取焦点的。其他的配置选项这里就不啰嗦了。

3.2在代码中加载XML文件

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

//setContentView(R.layout.activity_main);

addPreferencesFromResource(R.xml.item);

}

这样两步就可以构造一个设置界面了。

更多相关文章

  1. android实用技巧:android实现listview异步加载图片
  2. android ndk jni层访问java对象小结
  3. ListView中的CheckedTextView 多选/单
  4. android Button 的按下和抬起事件监听
  5. Android(安卓)Animation 高手必读 之一 Tweened Animations 代码
  6. Android(安卓)客户端通过HTTP POST发布图片和文字源代码
  7. Android(安卓)SwipeRefreshLayout RecyclerView
  8. Android(安卓)TextView 设置字与字之间的距离
  9. android 显示16色的图片:输入用颜色矩阵,显示对应的16色位图

随机推荐

  1. Delphi xe7 up1 调用android振动功能
  2. Android 开源组件和第三方库汇总
  3. Android studio 出现 Unsupported major.
  4. Android 弹出键盘向上顶布局
  5. android 如何判断去电或来电已经接通
  6. Android Binder的使用和设计[android nat
  7. AndroidStudio试用
  8. Android opencore 2.02 howto
  9. Android(安卓)APK签名
  10. android style的使用