input如何设置默认值值为 SwitchPreference 的 android 吗

本帖子已过去太久远了,不再提供回复功能。Android数据存储(二)----PreferenceFragment详解
一、PreferenceFragment的引入:
PreferenceActivity是一个非常有用的基类,当我们开发Android项目时避免不了选项设置,这些设置习惯用Preference来保存。
一、PreferenceFragment的引入:
PreferenceActivity是一个非常有用的基类,当我们开发Android项目时避免不了选项设置,这些设置习惯用Preference来保存。Android专门为这种Activity提供了便捷的基类PreferenceActivity。如果继承自Preference则不需要自己控制Preference的读写,PreferenceActivity会为我们处理一切。
PreferenceActivity与普通的Activity不同,它不是使用界面布局文件,而是使用选项设置的布局文件。
选项设置布局文件以PreferenceScreen作为根元素来表示定义一个参数设置界面布局。
从Android 3.0以后官方不再推荐直接让PreferenceActivity加载选项设置布局文件,而是建议使用PreferenceFragment,二者用法类似。所以今天就来学习一下。
下面的这张截图就是一个典型的例子:
我们会看到整个页面被分为几组:无线和网络、个人、账户、设备、系统。这个分组(或者叫分类)就是PreferenceCategory。
Wifi右边有开关,这一项就是CheckBoxPreference;其他还有ListPreference和EditTextPreference。
你的每一次设置,都会被Preference自动保存下来,这就是setting的数据持久化。每个Preference都是以键值对的形式保存下来的。
PreferenceActivity的继承关系图:(不推荐使用)
PreferenceFragment的继承关系图:(推荐使用)
二、在XML文件中Preference的种类:
根节点中一定是&PreferenceScreen& 元素,在这个元素中可以添加不同的Preference。常见的Preference控件有:
直接子类:DialogPreference, PreferenceGroup, RingtonePreference, TwoStatePreference
非直接子类:CheckBoxPreference, EditTextPreference, ListPreference, MultiSelectListPreference, PreferenceCategory, PreferenceScreen, SwitchPreference
我们还是通过实际的例子来实现吧。
先新建一个工程文件:
新建类PrefFragment.java,让其继承PreferenceFragment,并加载选项设置的布局文件:(核心代码是第6行和13行)
1 package com.example.m05_preffragment01;
3 import android.os.B
4 import android.preference.PreferenceF
6 public class PrefFragment extends PreferenceFragment {
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
//从xml文件加载选项
addPreferencesFromResource(R.xml.preferences);
然后,在MainActivity.java中加载上面的Fragment:
1 package com.example.m05_preffragment01;
3 import android.app.A
4 import android.app.FragmentM
5 import android.app.FragmentT
6 import android.os.B
8 public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//加载PrefFragment
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
PrefFragment prefFragment = new PrefFragment();
transaction.add(R.id.prefFragment, prefFragment);
接下来的知识就是重头戏了,既然PrefFragment要加载选项设置的布局文件R.xml.preferences,那我们就来定义一下这个preferences.xml:
在res目录下新建立一个xml目录,在该目录中新建文件preferences.xml:
关于preferences.xml的文件里面的代码,请看下面这一段。
五、preference详解:
1、&PreferenceCategory&的方式进行分组:
preferences.xml举例如下:
&?xml version="1.0" encoding="utf-8"?&
&PreferenceScreen xmlns:android="/apk/res/android" &
&!-- 设置的类别 --&
&PreferenceCategory
android:key="mylocation"
android:summary="我的位置"
android:title="我的位置源"&
&CheckBoxPreference
android:key="wireless_network"
android:summary="使用无线网络查看应用程序中的位置"
android:title="使用无线网络"/&
&/PreferenceCategory&
&PreferenceCategory
android:key="mymsg"
android:summary="个人信息"
android:title="个人信息"&
&EditTextPreference
android:key="myname"
android:title="请输入真实姓名"
android:summary="姓名"/&
&/PreferenceCategory&
&/PreferenceScreen&
上方代码中,一个&PreferenceCategory&就是一个类别,单个的&PreferenceCategory&可以放入任意的Preference控件。我们在第一个类别中放入了CheckBoxPreference控件,在第二个类别中放入了EditTextPreference控件。运行效果如下:
运行时候,会自动在/data/data/&packagename&/shared_prefs/目录生成一个文本文件:
注:这个文本的文件名太长了,我们可以在上方PrefFragment.java的第13行代码后面加上这样一句话来修改文件名:(这里我将文件名修改为:mysetting)
getPreferenceManager().setSharedPreferencesName("mysetting");
文本内容如下:
我们现在在CheckBoxPreference中打钩,然后在EditTextPreference中输入内容:
文本内容显示如下:
2、显示子屏幕subscreens方式的进行分组:
1 &?xml version="1.0" encoding="UTF-8"?&
2 &PreferenceScreen xmlns:android="/apk/res/android"
android:title="Settings" &
&PreferenceScreen
xmlns:android="/apk/res/android"
android:summary="settings about emotions"
android:title="Emotions" &
&CheckBoxPreference
android:defaultValue="true"
android:summaryOff="No,I am sorry."
android:summaryOn="Yes,I love you!"
android:title="Love me?" &
&/CheckBoxPreference&
&CheckBoxPreference
android:defaultValue="false"
android:summaryOff="No,you are a good person."
android:summaryOn="Yes,I hate you!"
android:title="Hate me?" &
&/CheckBoxPreference&
&/PreferenceScreen&
&PreferenceScreen
xmlns:android="/apk/res/android"
android:summary="settings about relations"
android:title="Relations" &
&CheckBoxPreference
android:defaultValue="true"
android:summaryOff="No,I am sorry."
android:summaryOn="Yes,we are family!"
android:title="Family?" &
&/CheckBoxPreference&
&CheckBoxPreference
android:defaultValue="false"
android:summaryOff="No,I am sorry."
android:summaryOn="Yes,we are friends!"
android:title="Friends?" &
&/CheckBoxPreference&
&/PreferenceScreen&
41 &/PreferenceScreen&
运行效果如下:
点击上方第一个preference,进入下图中左侧界面;点击上方第二个preference,进入下图中右侧界面。效果如下:
3、ListPreference控件:
点击该控件后,将列出一个单选按钮的列表。用法如下:
&?xml version="1.0" encoding="utf-8"?&
&PreferenceScreen xmlns:android="/apk/res/android" &
&PreferenceCategory
android:key="mylocation"
android:summary="我的位置"
android:title="我的位置源"&
&ListPreference
android:key="mycities"
android:title="所属城市"
android:summary="点击弹出城市列表"
android:dialogTitle="请选择城市"
android:entries="@array/cities"
android:entryValues="@array/cities"/&
&/PreferenceCategory&
&/PreferenceScreen&
上方代码中,PreferenceScreen为根标签,ListPreference为子标签。ListPreference的常见属性如下:
android:key 唯一标识符,和android:id相类似,PreferenceManager可以以其为参数通过findPreference获取指定的preference 。 注意,这个android:key的值也是Preference文件里面的XML“键”名
android:title 大标题
android:summary 标题下面的小字(这个要作为选项卡才有)
android:entries 弹出的对话框中,列表显示的文本内容,注意哦,这里指定的是一个数组。
android:entryValues 与android:entries相对应的值
android:defaultValue 当对应值不存在时的默认值
android:dialogTitle 弹出的对话框中的标题信息
关于android:entries和android:entryValues的区别,要强调一下:
android:entries:The human-readable array to present as a list. 是展现给用户的列表的值。
android:entryValues :he array to find the value to save for a preference when an entry from entries is selected. 展现的用户的选择列表的每个元素选择后,需要存储到手机中,这里的entryValues就是列表中各个元素被选择后存储到手机中的值(通过sharedPreferences保存在/data/data/&packagename&/shared_prefs/目录下)。简单的说就是此处是数据库中的值。上面的android:entries是展现给用户的列表的值。
我们在上方代码中的第13、14行引用了数据,紧接着我们要在values目录下的strings.xml文件中定义这个被引用的城市列表(即数据来源):
1 &?xml version="1.0" encoding="utf-8"?&
2 &resources&
&string name="app_name"&m05_PrefFragment01&/string&
&string name="action_settings"&Settings&/string&
&string name="hello_world"&Hello world!&/string&
&string-array name="cities"&
&item&成都&/item&
&item&重庆&/item&
&item&黄冈&/item&
&/string-array&
14 &/resources&
上方代码中,8至12行:即加粗部分,是我添加的数据源。
运行程序效果如下:
六、监听事件onPreferenceTreeClick()方法:
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen Preference preference)
当任何一个preference控件被点击,都将触发该方法。但是可以通过preference.getKey()这个方法找到具体是哪个preference被点击了,因为每个preference的key都是唯一的。
现将preferences.xml这个设置选项的布局文件,定义如下:
&?xml version="1.0" encoding="utf-8"?&
&PreferenceScreen xmlns:android="/apk/res/android" &
&!-- 设置的类别 --&
&PreferenceCategory
android:key="mylocation"
android:summary="我的位置"
android:title="我的位置源" &
&CheckBoxPreference
android:key="wireless_network"
android:summary="使用无线网络查看应用程序(例如Google地图)中的位置"
android:title="使用无线网络" /&
&CheckBoxPreference
android:key="gps_satellite_setting"
android:summary="定位时,精确到街道级别(取消选择可节约电量)"
android:title="启用GPS卫星设置" /&
&/PreferenceCategory&
&PreferenceCategory
android:key="mymessage"
android:summary="个人信息设置"
android:title="个人信息设置" &
&CheckBoxPreference
android:key="yesno_save_individual_info"
android:title="是否保存个人信息" /&
&EditTextPreference
android:key="individual_name"
android:summary="请输入真实姓名"
android:title="姓名 " /&
&ListPreference
android:entries="@array/cities"
android:entryValues="@array/cities"
android:key="mycity"
android:summary="所属城市"
android:title="所属城市" /&
&/PreferenceCategory&
&/PreferenceScreen&
运行效果如下:
现在我们需要做的是,选中上方红框中的CheckBoxPreference,对应下面的EditTextPreference为编辑状态;否则,如果CheckBoxPreference没有被选中,则EditTextPreference为不可编辑状态。修改PrefFragment.java的代码,其完整版代码如下:
1 package com.example.m05_preffragment01;
3 import android.os.B
4 import android.preference.CheckBoxP
5 import android.preference.EditTextP
6 import android.preference.P
7 import android.preference.PreferenceF
8 import android.preference.PreferenceS
10 public class PrefFragment extends PreferenceFragment {
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
//从xml文件加载选项
addPreferencesFromResource(R.xml.preferences);
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) {
//如果“保存个人信息”这个按钮被选中,将进行括号里面的操作
if("yesno_save_individual_info".equals(preference.getKey())) {
CheckBoxPreference checkBoxPreference = (CheckBoxPreference)findPreference("yesno_save_individual_info");
EditTextPreference editTextPreference = (EditTextPreference)findPreference("individual_name");
//让editTextPreference和checkBoxPreference的状态保持一致
editTextPreference.setEnabled(checkBoxPreference.isChecked());
// TODO Auto-generated method stub
return super.onPreferenceTreeClick(preferenceScreen, preference);
代码解释:
当任何一个preference控件被点击,都将触发onPreferenceTreeClick()方法(22行),但是可以通过preference.getKey()这个方法找到具体是哪个preference被点击(25行)。
26行、27行:通过public Preference findPreference(CharSequence key)找到对应的preference,然后强转为它的子类。
29行:核心代码,让editTextPreference和checkBoxPreference的状态保持一致。
运行效果如下:
这样,我们的功能就实现了。
版权声明:本文内容由互联网用户自发贡献,本社区不拥有所有权,也不承担相关法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至: 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
用云栖社区APP,舒服~
【云栖快讯】红轴机械键盘、无线鼠标等753个大奖,先到先得,云栖社区首届博主招募大赛9月21日-11月20日限时开启,为你再添一个高端技术交流场所&&
阿里云移动APP解决方案,助力开发者轻松应对移动app中随时可能出现的用户数量的爆发式增长、复杂的移动安全挑战等...
支持以数据库为核心的结构化存储产品之间的数据传输。 它是一种集数据迁移、数据订阅及数据实时同步于一体的数据传输服...
作为阿里云数据存储产品体系的重要组成部分,致力于提供低成本、高可靠的数据归档服务,适合于海量数据的长期归档、备份...
为您提供简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本...
MaxCompute75折抢购
Loading...1.1.1 Activity组件
本文所属图书&>&
如何才能真正进阶为Android应用开发高手?必须深入理解Android核心技术的底层原理和在开发中总结并使用各种最佳实践,别无他法!本书以Android的源代码为主,SDK为辅,针对应用开发者的需求,对各种核心技术的使...&&
1.1 核心详解
有4个核心,分别是:提供界面显示的Activtiy、提供后台计算的Service、提供进程间通信的Intent和提供广播接收的BroadcastReceiver。本节将详细介绍这些核心组件的使用方法和技巧,以及隐藏在背后的一些逻辑和原理。
1.1.1 Activity组件
Activity是的最重要组成部分之一,是实际与用户交互的组件。对于开发者而言,有几个非常重要的Activity子类需要特别注意,如ListActivity、PreferenceActivity和TabActivity。Activity的类图如图1-1所示,Activity的生命周期的相关内容将会在1.2.2节中介绍。
下面介绍图1-1所示类的用途和实现原理。
1. ListActivity
ListActivity可以用来实现列表功能。列表可以说是终端应用最基本的控件,几乎每个应用中都会存在列表。但ListActivity也是最复杂的控件之一,其丰富的使用场景使很多初学者需要很长的时间才能熟练使用它。
在Android中,ListActivity提供了对基本的单行、双行列表的封装,同时还支持用户自定义列表。自定义列表主要是基于ListView来实现的,为了简便起见,在此一并介绍。
就实现而言,Android充分利用了设计模式中的适配器模式(Adaptor Pattern),无论数据多么复杂,总可以做到以不变应万变,极大地减小了实现的复杂度。
实现一个列表包括3步:选择或自定义列表项布局文件、实现适配器并加载数据、为ListActivity设置适配器。适配器的实现将在3.10.1节中重点介绍。
在整个列表中,列表项布局文件决定了列表的布局风格,很多初学者容易在实现自定义列表时犯布局兼容性的错误,即在选择布局控件时,不考虑所显示数据长度是基本固定的还是变化的,导致目标环境的适配出现问题。事实上,只要熟练掌握各布局控件的特点,就可轻松做出正确的选择。最基本、最常用的布局控件只有RelativeLayout和LinearLayout两个。
适配器的选择也非常简单,目前Android支持两种基本类型的适配器:基本适配器(BaseAdapter)和游标适配器(CursorAdapter)。基本适配器是最通用的适配器,游标适配器是用来适配的数据流的,其他的级适配器都是继承自这两种适配器。
(1)列表项布局文件
在Android中,目前已经实现了基本的列表项布局文件,分别是基于单行布局的simple_list_item_1布局文件、基于简单双行布局的simple_list_item_2布局文件、基于单行单选布局的simple_list_item_single_choice布局文件、基于单行多选布局的simple_list_item_multiple_choice布局文件、类似树状图的simple_expandable_list_item_1和simple_expandable_list_item_2布局文件等。出于介绍方便的考虑,本书将这些基于系统已有风格的列表称为系统列表。
基于simple_list_item_1布局文件的一个实现如下:
// mStrings为数组类型的数组
setListAdapter(new ArrayAdapter&String&(this,
&&& android.R.layout.simple_list_item_1, mStrings));
下面以ArrayAdapter为例来介绍适配器加载布局文件的方法,具体如代码清单1-1所示。
代码清单 1-1 ArrayAdapter加载布局文件的实现代码
private View createViewFromResource(int position, View convertView, ViewGroup parent,int resource) {
if (convertView == null) {
&& //加载布局文件
&&& view = mInflater.inflate(resource, parent, false);&
&&& view = convertV
&&& if (mFieldId == 0) {&
//判断是否有指定的mFieldId,如果没有,则假定整个布局文件为一个TextView
&&&&&& text = (TextView)
&&&&& } else {
&&&&&&& //否则,将数据加载到指定控件ID的TextView上
&&&&&&& text = (TextView) view.findViewById(mFieldId);
&& } catch (ClassCastException e) {
&&&& Log.e(&ArrayAdapter&, &You must supply a resource ID for a TextView&);
&&&& throw new IllegalStateException(
&&&&&&&&& &ArrayAdapter requires the resource ID to be a TextView&, e);
&& T item = getItem(position);
&& if (item instanceof CharSequence) {
&&&&&& text.setText((CharSequence)item);
&& } else {
&&&& text.setText(item.toString());
从ArrayAdapter加载布局文件的过程可以分析出,在默认情况下,ArrayAdapter假定整个布局文件为一个TextView,只有指定了mFieldId,即通过如下方法初始化ArrayAdapter时,ArrayAdapter才认为加载了一个自定义布局。
public ArrayAdapter(Context context, int resource, int textViewResourceId, List&T& objects)
// textViewResourceId即mFieldId
ArrayAdapter的实现有很强的局限性,仅能显示单行的列表。下面介绍Android实现双行的列表的方法。
SimpleCursorAdapter的初始化过程如下:
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
&&&&& android.R.layout.simple_list_item_2, c,
&&&&&&&&& new String[] {
&&&&&&&&&& Phone.TYPE,
&&&&&&&&&& Phone.NUMBER
&&&&&&&& }, new int[] { android.R.id.text1, android.R.id.text2 });
SimpleCursorAdapter的ViewBinder的实现如下:
adapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
&&&& public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
&&&& if (columnIndex != COLUMN_TYPE) {
&&&&& int type = cursor.getInt(COLUMN_TYPE);
&&&&& String label =
&&&& if (type == Phone.TYPE_CUSTOM) {
&&&&&&&&&& label = cursor.getString(COLUMN_LABEL);
&&&& String text = (String) Phone.getTypeLabel(getResources(), type, label);
&&&& ((TextView) view).setText(text);
在ViewBinder的实现过程中指定了布局文件、游标、数据项、控件ID等,但没有处理布局加载和数据绑定。下面是SimpleCursorAdapter的bindView()的实现,该方法揭示了布局加载和数据绑定的过程。
public void bindView(View view, Context context, Cursor cursor) {
&&& final ViewBinder binder = mViewB
&&& final int count = mTo.&& //count为控件的数量
&&& final int[] from = mF
&&& final int[] to = mTo;
&&& for (int i = 0; i & i++) {&
&&&&& final View v = view.findViewById(to[i]);& //获得控件句柄
&&&&& if (v != null) {
&&&&&&&&& boolean bound =
&&&&&&&&& if (binder != null) {
&&&&&&&& //自定义绑定数据
&&&&&&&&& bound = binder.setViewValue(v, cursor, from[i]);&&&&&&&&&&
&&&&&&&&& if (!bound) {&&//默认绑定数据
&&&&&&&&&&&&& String text = cursor.getString(from[i]);
&&&&&&&&&&&&&& if (text == null) {
&&&&&&&&&&&&&&&&& text = &&;
&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&& if (v instanceof TextView) {
&&&&&&&&&&&&&&&& setViewText((TextView) v, text); //设置文本数据
&&&&&&&&&&&&&& } else if (v instanceof ImageView) {
&&&&&&&&&&&&&&&& setViewImage((ImageView) v, text); //设置图片数据
&&&&&&&&&&&&&& } else {
&&&&&&&&&&&&&&&& throw new
&&&&&&&&&&&&&&&& IllegalStateException(v.getClass().getName() + & is not a & +
&&&&&&&&&&&&&&&&&&& & view that can be bounds by this SimpleCursorAdapter&);
&&&&&&&&&&&&&& }
&&&&&&&&&&&& }
&&&&&&&&&& }
从bindView()的实现中可以看出,SimpleCursorAdapter的ViewBinder实际上是用户的一个自定义实现接口,当用户没有进行自定义实现时,SimpleCursorAdapter会通过传递的控件数量进行默认数据绑定。可以颠覆大多数初学者固有认识的是,SimpleCursorAdapter还支持图片加载,不过这时通过游标获取的不再是文本数据,而是图片的URI。
从bindView()的实现中还可以看出,SimpleCursorAdapter绝不仅仅支持simple_list_item_2布局,还支持更多的数据项。事实上,不读源代码的初学者很容易将思维局限于Android的自带示例和若干入门书籍的示例内,使Android的系统实现能力无法被充分利用。现在可以了解到,在进行普通数据加载时比较复杂的场景也可以利用SimpleCursorAdapter而非自定义适配器来实现。当默认数据绑定无法满足需求时,可使用ViewBinder。
自定义列表项布局文件非常简单,本书就不再介绍了。
(2)系统适配器
在Android中提供了多个适配器可供开发者选择,主要有BaseAdapter、CursorAdapter、ResourceCursorAdapter、SimpleCursorAdapter、ArrayAdapter和SimpleAdapter等。如图1-2所示为基本适配器的类图。
BaseAdapter适配器是最基本的适配器,是其他适配器的基类。BaseAdapter适配器可以适配ListView和Spinner等控件。在实际的开发中,通常会调用BaseAdapter的子类或通过BaseAdapter自定义适配器来实现视图与数据的适配功能。
在BaseAdapter适配器中,应用了设计模式中的观察者模式(Observer Pattern),当数据源发生变化时,可以通知显示控件自行刷新,具体实现如图1-3所示。
通过分析源代码可以发现,AdapterDataSetObserver是AdapterView的一个私有类,在开发者为ListView设置适配器时,ListView会通过适配器注册观察器,当开发者调用适配器的notifyDataSetChanged()方法时,ListView会收到数据源变化的通知,进行自行刷新。其自行刷新的实现代码如下:
public void onChanged() {
&& mDataChanged =
&& mOldItemCount = mItemC
&& mItemCount = getAdapter().getCount();
&& if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
&&&&&&&& && mOldItemCount == 0 && mItemCount & 0) {
&&&&&& AdapterView.this.onRestoreInstanceState(mInstanceState);
&&&&&& mInstanceState =
&&& } else {
&&&&&& rememberSyncState();
&& checkFocus();
&& requestLayout();&&//执行刷新
部分开发者在不了解适配器采用观察者模式的情况下,会再次调用setAdapter()以实现视图的刷新,但从setAdapter()的实现可知,虽然这种方法也可实现刷新,但其在性能上的损耗比调用适配器的notifyDataSetChanged()方法要高。
注意 在数据库适配器中数据发生变化时(如增加或删除列表项),应重新获取游标(Cursor),然后再调用notifyDataSetChanged()方法进行数据刷新。
在Gingerbread中,Google还引入了setRemoteViewsAdapter ()和RemoteViewsService建立连接,为跨进程的数据适配提供支持。
(3)自定义适配器
虽然Android提供了多个系统布局文件和适配器,但现实中的应用总存在其独特的一面,尤其是在实现更多的个性化设计时,自定义列表更多是通过自定义适配器来实现的。这里主要介绍两种自定义适配器的实现。
1)基于BaseAdapter的自定义适配器
对于基于BaseAdapter的自定义适配器,需要重点关注getView()方法的实现,示例如代码清单1-2所示。
代码清单1-2 基于BaseAdapter的自定义适配器
private class MyListAdapter extends BaseAdapter {
&&& public MyListAdapter(Context context) {
&&&&&&& mContext =
&&& public int getCount() {
&&&&&&& return mStrings. // mStrings为一个数组类型的数据源
&&& @Override
&&& public boolean areAllItemsEnabled() {
&&& @Override
&&& public boolean isEnabled(int position) {
&&&&&& return !mStrings[position].startsWith(&-&);
&&& public Object getItem(int position) {
&&&&&& return mStrings[position];&//返回当前位置的Item
&&& public long getItemId(int position) {
&&&&&& //返回当前的ID,通常用于游标适配器
&&& public View getView(int position, View convertView, ViewGroup parent) {
&&&&&& TextV
&&&&&& if (convertView == null) {
&&&&&&&&&& tv = (TextView) LayoutInflater.from(mContext).inflate(
&&&&& android.R.layout.simple_expandable_list_item_1, parent, false);
&&&&&& } else {
&&&&&&&&&& tv = (TextView) convertV
&&&&&& tv.setText(mStrings[position]);
&&&& private Context mC
getView()方法完成的主要工作是列表项布局文件的加载和数据的绑定。需要注意的是,只在convertView为空时加载布局文件,这样可避免无谓的性能损耗,这是优化ListView显示的一个重要方法。
2)基于CursorAdapter的自定义适配器
基于CursorAdapter的自定义适配器的实现重点在于bindView()方法和newView()方法。其中bindView()方法用于绑定数据,newView()方法用于加载布局文件,示例如下:
public class TagAdapter extends CursorAdapter {
&&& private final LayoutInflater mI
&&& public TagAdapter(Context context) {
&&&&&&& super(context, null, false);
&&&&&&& mInflater = LayoutInflater.from(context);
&&& @Override
&&& public void bindView(View view, Context context, Cursor cursor) {
&&&&&&& ViewHolder holder = (ViewHolder) view.getTag();
&&&&&&& CharArrayBuffer buf = holder.titleB
&&&&&&& cursor.copyStringToBuffer(TagQuery.COLUMN_TITLE, buf);
&&&&&&& holder.mainLine.setText(buf.data, 0, buf.sizeCopied);&& holder.dateLine.setText(DateUtils.getRelativeTimeSpanString(
&&&&&&& context,cursor.getLong(TagQuery.COLUMN_DATE)));
&& @Override
&& public View newView(Context context, Cursor cursor, ViewGroup parent) {
&&&&& View view = mInflater.inflate(R.layout.tag_list_item, null);
&&&&& // 为视图提供缓冲,优化渲染速度
&&&&& ViewHolder holder = new ViewHolder();
&&&&& holder.titleBuffer = new CharArrayBuffer(64);
&&&&& holder.mainLine = (TextView) view.findViewById(R.id.title);
&&&&& holder.dateLine = (TextView) view.findViewById(R.id.date);
&&&&& view.setTag(holder);
&&& @Override
&&& public void onContentChanged() {
&&&&&& new TagLoaderTask().execute((Void[]) null);
考虑到加载列表项时多次操作findViewById()方法,对性能有所影响,因此在Android中设计了ViewHolder,以其来进行优化。通过View的setTag()方法和getTag()方法可大幅度提高显示速率,这同样是优化ListView显示的一种重要方法。
(4)复杂场景处理
除了正常的布局加载和数据适配外,在ListView中还有一些特殊的场景需要开发者注意,如快速滚动时的显示问题、列表项中可单击控件的处理等。下面进行简单介绍。
1)快速滚动时的显示问题
在快速滚动时,对于性能稍弱的设备或计算量大的渲染,可能无法及时进行处理,影响用户的体验效果。Android中快速滚动的列表显示的示例实现如代码清单1-3所示。
代码清单1-3 快速滚动的列表显示
public class ScrollActivity extends ListActivity {
&&& /** Called when the activity is first created. */
&& private boolean mBusy =&//状态变量
&&& @Override
&&& public void onCreate(Bundle savedInstanceState) {
&&&&&&& super.onCreate(savedInstanceState);
&&&&&&& setListAdapter(new ScrollAdapter(this));
&&&&&&& getListView().setOnScrollListener(new OnScrollListener(){
&&&&&&& public void onScroll(AbsListView view, int firstVisibleItem,
&&&&&&&&&&&&& int visibleItemCount, int totalItemCount) {
&&&&&&&&&&&&&& public void onScrollStateChanged(AbsListView view, int scrollState) {
&&&&&&&&&&&&&&&&& switch (scrollState) {
&&&&&&&&&&&&&&&&&&&& case OnScrollListener.SCROLL_STATE_IDLE:
&&&&&&&&&&&&&&&&&&&&&& mBusy =
&&&&&&&&&&&&&&&&&&&&&& ...
&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&& case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
&&&&&&&&&&&&&&&&&&&&&& mBusy =&&//判断正在滚动
&&&&&&&&&&&&&&&&&&&&&& ...
&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&& case OnScrollListener.SCROLL_STATE_FLING:
&&&&&&&&&&&&&&&&&&&&&& mBusy =&&& //判断正在滚动
&&&&&&&&&&&&&&&&&&&&&& ...
&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&&&& }
private class ScrollAdapter extends BaseAdapter {
&&&&& public int getCount() {
&&&&&&& return 0;
public Object getItem(int position) {
public long getItemId(int position) {
& return 0;
public View getView(int position, View convertView, ViewGroup parent) {
&&& if(mBusy){&& //对滚动状态做出相应处理
&&&&&& ...
&&& }else{
&&&&& ...&
&&& return convertV
2)列表项中可单击控件的处理
对于复杂的列表项布局文件,如果其中包含了可单击控件,且默认子控件会自动获得焦点,则会导致在单击列表项时,列表项本身无法处理单击事件。这种情况给很多初学者带来了困惑,在处理时往往不得要领。以Button控件为例,正确的解决办法是在适配器的getView()方法中调用Button的setFocusable()方法,使Button无法获得焦点。
3)无列表项时的ListView显示
在ListView中,尤其是在网络应用中,无任何列表项可以显示是经常出现的一个场景,为了保证用户体验,需要给用户适当的提示。Android对此种场景同样提供了支持,实现的方法非常简单,具体如下:
public class EmptyListActivity extends ListActivity {
&&& public void onCreate(Bundle savedInstanceState) {
&&&&&&& super.onCreate(savedInstanceState);
&&&&&&& setContentView(R.layout.empty_list);
empty_list.xml的实现如下:
&FrameLayout
& xmlns:android=&/apk/res/android&
& android:layout_width=&match_parent&
& android:layout_height=&match_parent&&
& &ListView
&&& android:id=&@android:id/list&
&&& android:layout_width=&match_parent&
&&& android:layout_height=&match_parent&/&
//设置empty控件
& &TextView android:id=&@android:id/empty&
&&& android:layout_width=&match_parent&
&&& android:layout_height=&match_parent&
&&& android:text=&@string/status_no_item&
&&& android:gravity=&center&
&&& android:textStyle=&italic&
android:textAppearance=&@android:style/TextAppearance.Large&/&
&/FrameLayout &
如果开发者希望干预无列表项时的ListView显示,则需要关注适配器的isEmpty()方法。当isEmpty()方法返回true时,表示ListView当前处于无列表项的状态。
在HoneyComb中处理无列表项的方法更加简单,主要是通过ListFragment的setEmptyText ()方法来实现。
4)设置头视图和尾视图
对于一些特殊的场景,可能需要设置头视图和尾视图。下面是一种添加头视图的方法:
private void loadHeaderView() {
& LayoutInflater inflater = LayoutInflater.from(this);
& LinearLayout layout = (LinearLayout) inflater.inflate(R.layout.lib_list_header, null, false);
& getListView().addHeaderView(layout);
注意 如果需要添加头视图或尾视图,必须在添加完相应的视图后才能设置适配器,而且对于加载了的头视图和尾视图的列表,如果在刷新时无任何列表项可以显示,头视图和尾视图也会消失。
2. PreferenceActivity
简单来讲,PreferenceActivity主要用于实现偏好设置,在布局上PreferenceActivity以PreferenceScreen为根布局,支持CheckBoxPreference等多种形式的偏好设置。这些偏好值默认存储于应用的SharedPreferences中,通过getSharedPreferences()可以获取SharedPreferences对象,通过Preference.OnPreferenceChangeListener监听器可以监听到偏好值的变化。
另外在Android 4.0中,还引入了SwitchPreference类。SwitchPreference是一个存储布尔值的SharedPreferences。
下面介绍几种偏好设置的实现。
(1)CheckBoxPreference
CheckBoxPreference不但提供了进行二选一偏好的方法,还支持偏好的说明。下面是CheckBoxPreference布局文件的一个示例:
&CheckBoxPreference
&&& android:key=&bt_discoverable&
&&& android:title=&@string/bluetooth_visibility&
&&& android:dependency=&bt_checkbox&
&&& android:summaryOn=&@string/bluetooth_is_discoverable&//偏好说明
&&& android:summaryOff=&@string/bluetooth_not_discoverable&
&&& android:persistent=&false& /&
(2)DialogPreference
DialogPreference在Android中目前仅作为一个接口存在,如果开发者想借助DialogPreference实现更加复杂的偏好,则必须继承DialogPreference。这在仅进行文字性提示的情况下显得不大方便,好在继承DialogPreference十分简单,其具体方法如下:
public class AboutDialogPreference extends DialogPreference{
&public AboutDialogPreference(Context context, AttributeSet attrs) {
& super(context, attrs);
进行以上操作后才可以设置基本的文本信息。由此来看,这是Google在设计DialogPreference时的一个缺陷。
(3)EditTextPreference
EditTextPreference提供了支持输入框的偏好设置功能。通过getEditText()方法可以获得输入框的内容,通过getText()方法可以获得SharedPreferences中存储的偏好值,通过setText()方法可以将偏好值保存在SharedPreferences中,具体方法如下:
&EditTextPreference
&&&& android:key=&@string/display_name&
&&&& android:title=&@string/display_name_title&
&&&& android:dialogTitle=&@string/display_name_title&
&&&& android:summary=&@string/display_name_summary&
&&&& android:persistent=&false&
&&&& android:singleLine=&true&/&
(4)ListPreference
当某个偏好有多个偏好值可选时,ListPreference就派上用场了。在使用ListPreference时需要注意entries和entryValues属性,其中entries表示界面的内容,而entryValues对应的是实际偏好值。下面是一个传输协议类型的偏好配置:
&ListPreference
&&& android:key=&@string/transport&
&&& android:title=&@string/transport_title&
&&& android:entries=&@array/transport_types&
&&& android:entryValues=&@array/transport_types&
&&& android:summary=&@string/default_transport&
&&& android:persistent=&false&
&&& android:dialogTitle=&@string/transport_title&/&
在如下选项数组中包含UDP、TCP两个偏好值,在通常的场景中,entryValues属性值通常包含的是字符串类型的数字。
&string-array translatable=&false& name=&transport_types&&
&& &item&UDP&/item&
&& &item&TCP&/item&
&/string-array&
在ListPreference中,用户只能做单选操作,如果期望能够执行多选操作,则需要用到MultiSelectListPreference。MultiSelectListPreference的资源文件配置和ListPreference类似,这里就不多做介绍了。
(5)RingtonePreference
RingtonePreference是一个用于设置铃声的特殊偏好控件,目前Android提供的铃声类型包括ringtone、notification、alarm和all等。其中all表示所有可用的铃声。下面是一个RingtonePreference的配置示例:
&RingtonePreference
&&& android:layout=&?android:attr/preferenceLayoutChild&
&&& android:key=&preferences_alerts_ringtone&
&&& android:title=&@string/preferences_alerts_ringtone_title&
&&& android:ringtoneType=&notification&
&&& android:defaultValue=&content://settings/system/notification_sound& /&
(6)PreferenceCategory
PreferenceCategory提供了偏好组的功能。在Android中,PreferenceCategory同样是个常用的偏好控件。下面是一个PreferenceCategory的配置示例:
&PreferenceCategory
&&& android:key=&account_servers&
&&& android:title=&@string/account_settings_servers&&
&&& &PreferenceScreen
&&&&&&& android:key=&incoming&
&&&&&&& android:title=&@string/account_settings_incoming_label& /&
&&& &PreferenceScreen
&&&&&&& android:key=&outgoing&
&&&&&&& android:title=&@string/account_settings_outgoing_label& /&
&&& &CheckBoxPreference
&&&&&&& android:layout=&?android:attr/preferenceLayoutChild&
&&&&&&& android:key=&account_sync_contacts&
&&&&&&& android:defaultValue=&true&
&&&&&&& android:title=&@string/account_settings_sync_contacts_enable&
&&&&&&& android:summary=&@string/account_settings_sync_contacts_summary& /&
&&& &CheckBoxPreference
&&&&&&& android:layout=&?android:attr/preferenceLayoutChild&
&&&&&&& android:key=&account_sync_calendar&
&&&&&&& android:defaultValue=&true&
&&&&&&& android:title=&@string/account_settings_sync_calendar_enable&&&&&
&&&&&&& android:summary=&@string/account_settings_sync_calendar_summary& /&
&/PreferenceCategory&
3. TabActivity
在有限的空间中,尤其是在显示区域有限的情况下,实现丰富的功能始终是交互设计的一个难题。在早期的功能手机平台中,类似的问题只能通过更深的路径来解决,幸运的是,Android系统提供了TabActivity,能够让用户在单一界面中实现更多的功能,简化用户的操作。
TabActivity的根布局控件为TabHost,它由TabWidget和通常基于FrameLayout的内容显示区域组成。下面是电话簿应用中TabActivity的一个布局文件dialer_activity.xml的实现。
&xmlns:android=&/apk/res/android&
&&& android:id=&@android:id/tabhost&& //系统ID
&&& android:layout_width=&match_parent&
&&& android:layout_height=&match_parent&&
&&& &LinearLayout
&&&&&&& android:orientation=&vertical&
&&&&&&& android:layout_width=&match_parent&
&&&&&&& android:layout_height=&match_parent&&
&&&&&&& &TabWidget android:id=&@android:id/tabs&//系统ID
&&&&&&&&&&& android:layout_width=&match_parent&
&&&&&&&&&&& android:layout_height=&wrap_content&
&&&&&&& /&
&&&&& &FrameLayout android:id=&@android:id/tabcontent&//系统ID
&&&&&&&&&&& android:layout_width=&match_parent&
&&&&&&&&&&& android:layout_height=&0dip&
&&&&&&&&&&& android:layout_weight=&1&
&&&&&&& /&
&&& &/LinearLayout&
&/TabHost&
(1)自定义Tab
在MDPI和LDPI密度下,交互设计师通常希望能够增加有效显示的区域;而对于标准的Tab而言,它由图标和标签两部分组成。图标的存在减小了有效显示的区域面积,因此在部分场景中交互设计师希望仅保留标签。对于这两种实现,Android均提供了支持,拥有图标和标签的Tab的实现如下:
private void setupCallLogTab() {
Intent intent = new
Intent(&com.android.phone.action.RECENT_CALLS&);
intent.setClass(this, RecentCallsListActivity.class);
&&& StickyTabs.setTab(intent, TAB_INDEX_CALL_LOG);
&&& mTabHost.addTab(mTabHost.newTabSpec(&call_log&)
&&& .setIndicator(getString(R.string.recentCallsIconLabel),&&&&&&&&&&&
&&& getResources().getDrawable(R.drawable.ic_tab_recent))
&&&&&&&&&&&&&&&& .setContent(intent));
仅拥有标签的Tab实现如下:
final Intent tabIntent = new Intent(getIntent());
tabIntent.setComponent((ComponentName)
tabIntent.getParcelableExtra(&tab&));
final TabHost th = getTabHost();
final TabHost.TabSpec ts = th.newTabSpec(&1&);
ts.setIndicator(&One&);
ts.setContent(tabIntent);
th.addTab(ts);
如果希望自定义Tab的高度等信息,方法如下:
for (int i =0; i & getTabWidget().getChildCount(); i++) {
&& getTabWidget().getChildAt(i).getLayoutParams().height = 48;
&& TextView tv = (TextView)getTabWidget()
.getChildAt(i).findViewById(android.R.id.title);
&& tv.setTextSize(20);
(2) 创建Tab页
在Android中,Tab页的创建有两种方式:一种是加载视图的方式,一种是加载Activity的方式。
1)加载视图
对于布局简单的Tab页,通常采用加载视图的方式实现。在Android中,主要通过createTabContent()方法来实现,示例如下:
public View createTabContent(String tag) {
&& View mLayout =
&&& if (tag.equals(&tab1&) == true) {
&&&&& mLayout =& mInflater.inflate(R.layout. tab1, null);
&&& } else if (tag.equals(&tab2&) == true) {
  mLayout =& mInflater.inflate(R.layout. tab2, null);
&& return mL
2)加载Activity
对于布局复杂的Tab页,为了将复杂性降低,通常采用加载Activity的方式实现。这需要用到tabHost 的setContent()方法,示例如下:
protected void onCreate(Bundle savedInstanceState) {
&&&& super.onCreate(savedInstanceState);
&&&&&&& final TabHost tabHost = getTabHost();
&&&&&&& tabHost.addTab(tabHost.newTabSpec(&tab1&)
&&&&&&&&&&&&&&& .setIndicator(&list&)
&&&&&&&&&&&&&&& .setContent(new Intent(this, List1.class)));
&&&&&&& tabHost.addTab(tabHost.newTabSpec(&tab2&)
&&&&&&&&&&&&&&& .setIndicator(&photo list&)
&&&&&&&&&&&&&&& .setContent(new Intent(this, List8.class)));
&&&&&&& tabHost.addTab(tabHost.newTabSpec(&tab3&)
&&&&&&&&&&&&&&& .setIndicator(&destroy&)
&&&&&&&&&&&&&&& .setContent(new Intent(this, Controls2.class)
&&&&&&&&&&&&&&& .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)));
4. Activity的加载模式
在Android中,目前Activity共有4种加载模式:standard、singleTop、singleTask和singleInstance。
standard加载模式是Activity的默认加载模式,在加载时会创建一个新的Activity的实例,类似于调用startActivity()方法时设置Intent的标志位为Intent.FLAG_ACTIVITY_NEW_TASK。发起 standard加载模式的示例如下:
Intent i = new Intent();
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
singleTop加载模式表示当前Activity的实例处于前台并可视时,该实例会收到发送过来的Intent消息,其接收方法如下:
protected void onNewIntent(Intent intent) {
& super.onNewIntent(intent);
singleTask加载模式表示当前Activity栈(Task)中当前Activity实例运行时,该实例会收到相应的Intent消息,接收方法类似于singleTop加载模式。发起singleTask加载模式的示例如下:
Intent i = new Intent();
i.setFlags(Intent. FLAG_ACTIVITY_BROUGHT_TO_FRONT);
startActivity(i);
singleInstance加载模式表示该Activity以单子模式加载,在当前Activity栈中唯一,接收方法类似于singleTop加载模式。
为了保证Activity在系统运行时能正常加载,必须在AndroidManifest.xml中声明所有需要加载的Activity,方法如下:
&activity android:name=&TestActivity&
&& android:label=&@string/app_name&
&& android:launchMode=&singleInstance&
& //忽略重力传感器和键盘隐藏等变化
& android:configChanges=&orientation|keyboardHidden&&
&/activity&
在通常情况下,当单个应用中包含的文件较多时,通常需创建子文件夹进行区分,如对com.miaozl.test应用而言,将实现Activity的文件放在ui文件夹下,将实现Service、Provider的文件放在service文件夹下,将实现的一些自定义视图放在view文件夹下,将实现的一些工具文件放置在util文件夹下。这样在加载Activity等时,声明的方法略有不同,具体如下:
//注意&ui&前面的&.&,表示当前目录
&activity android:name=&.ui.TestActivity&
&android:label=&@string/app_name&
&android:configChanges=&orientation|keyboardHidden&&
&/activity&
5. Activity的属性配置
除了支持android:launchMode属性外,Activity还支持多种属性,如android: configChanges、android:exported、 android:screenOrientation、android:theme等。下面将对重要的属性进行介绍,与Task相关的属性将在1.2.2节中介绍。
(1)配置变化属性
在Android中,存在一些基本的配置,如 android:mcc、 android:mnc、 android:locale、 android:touchscreen、android:keyboard、android:keyboardHidden、android:navigation、 android:orientation、android:screenLayout、android:fontScale和android:uiMode等。用户可以对这些属性进行配置,如果实现与配置相关,则需监听这些属性的变化。下面对上述属性做简要介绍。
&android:mcc属性表示MCC(Mobile Country Code),即SIM卡中存储的IMSI号中的国家代码部分,当发生国际漫游时,该属性配置会发生变化。
&android:mnc属性表示MNC(Mobile Network Code),即SIM卡中存储的IMSI号中的网络代码部分,当发生不同运营商网络间漫游时,该配置会发生变化,如在中国移动和中国联通的GSM网络间漫游。当然,运营商可以限制此类漫游。
&android:locale属性表示当前显示语言发生变化。
&android:touchscreen属性表示触摸屏发生了变化,考虑到当前的实际情况,只有在支持多屏显示时才需使用该属性。
&android:keyboard属性表示键盘类型发生了变化,如外接了蓝牙键盘等。
&android:keyboardHidden属性表示显示或者隐藏键盘,对翻盖、滑盖终端有效。
&android:navigation属性表示导航键发生了变化,如从轨迹球变化为五向键,考虑到当前的实际情况,该配置显然不会发生变化。
&android:orientation属性表示屏幕方向,在当前重力传感器几乎已经成为智能终端标配的情况下,需对该配置多加注意。
&android:screenLayout属性表示屏幕布局发生变化。在不支持多屏显示的情况下,该配置不会发生变化。
&android:fontScale属性表示字体发生了变化。
&android:uiMode属性表示UI模式发生了变化,如变为车载模式、夜间模式等,该配置在Android 2.2中引入。
在默认情况下,当配置发生变化时,当前的Activity会在被销毁后重新生成,通过在AndroidManifest.xml中为相应的Activity针对特定配置声明该属性,则可阻止特定配置变化时Activity被销毁后重新生成。当然,在某些场景下,开发者仍希望能监听到配置变化,这可在Activity的如下方法中实现:
public void onConfigurationChanged(Configuration newConfig)
下面是packages\apps\mms\src\com\android\mms\ui\目录下的ComposeMessageActivity.java中的一个示例:
public void onConfigurationChanged(Configuration newConfig) {
&&& super.onConfigurationChanged(newConfig);
&&& mIsKeyboardOpen = newConfig.keyboardHidden == KEYBOARDHIDDEN_NO;
&&& boolean isLandscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
&&& if (mIsLandscape != isLandscape) {
&&&&&& mIsLandscape = isL
&&&&&& ...
&&& onKeyboardStateChanged(mIsKeyboardOpen);
android:configChanges属性的设置方法如下:
android:configChanges=&orientation|keyboardHidden&
(2)屏幕旋转属性
android:screenOrientation属性是Activity的一个重要属性,在有重力传感器的情况下,开发者必须考虑屏幕的适配情况。其属性值包括unspecified、landscape、portrait、user、behind、sensor和nosensor等。其中unspecified为默认值,旋转策略由系统决定;landscape表示横屏;portrait表示竖屏;user表示当前用户倾向的屏幕方向;behind表示屏幕方向和Activity栈中当前Activity下面的Activity相同;sensor表示根据重力传感器确定屏幕方向,这是一个重要的属性值;nosensor表示忽略传感器方向。android:screenOrientation属性的设置方法如下:
android:screenOrientation=&nosensor&
(3)主题属性
android:theme属性表示当前Activity的主题,这是一个非常重要的属性,通常用于设置标题栏、状态栏等,当然也可设置开发者自定义的主题。该属性的设置方法如下:
android:theme=&@android:style/Theme.NoTitleBar&&
(4) 启动约束属性
android:exported属性表示启动约束,即是否允许被其他进程调用,如果值为false,则该Activity仅可被同一应用中的组件或拥有相应用户ID的应用的组件调用;如果值为true,则允许被其他进程调用。
android:exported属性的默认值与携带的Intent过滤器有关,如果没有携带任何Intent过滤器,则其值为false,否则为true。
您对本文章有什么意见或着疑问吗?请到您的关注和建议是我们前行的参考和动力&&
您的浏览器不支持嵌入式框架,或者当前配置为不显示嵌入式框架。
文章下载读书

我要回帖

更多关于 数据库如何设置默认值 的文章

 

随机推荐