我们在使用Android手机时,可能会经常用到其中的设置界面,如下所示:
图1 Android系统中的设置界面
而且对于我们自己开发的一个完整的程序来说,我们也会需要给用户提供一个设置界面使用户可以对程序的一些参数进行设置。因此,我们就来学习下如何实现符合我们需要的一个设置界面,及其背后的数据持久化功能。
在具体介绍Android的设置界面的实现之前,我们先来介绍一下预备知识,就是Android数据持久化方法中最简单的一种,即使用Preferences的键值对存储方式。这种方式主要用来存储比较简单的一些数据,而且是标准的Boolean、Int、Float、Long、String等类型。
android.content.SharedPreferences是一个接口,用来获取和修改持久化存储的数据。有三种获取系统中保存的持久化数据的方式:
1. public SharedPreferences getPreferences (int mode)
通过Activity对象获取,获取的是本Activity私有的Preference,保存在系统中的xml形式的文件的名称为这个Activity的名字,因此一个Activity只能有一个,属于这个Activity。
2. public SharedPreferences getSharedPreferences (String name, int mode)
因为Activity继承了ContextWrapper,因此也是通过Activity对象获取,但是属于整个应用程序,可以有多个,以第一参数的name为文件名保存在系统中。
3. public static SharedPreferences getDefaultSharedPreferences (Context context)
PreferenceManager的静态函数,保存PreferenceActivity中的设置,属于整个应用程序,但是只有一个,Android会根据包名和PreferenceActivity的布局文件来起一个名字保存。
通过以上方式取得SharedPreferences后就可以对数据进行读取或者保存了。
保存方式如下:
获得SharedPreferences,如果需要进行保存等修改操作,首先得通过其edit()方法获得SharedPreferences.Editor,然后就可以通过putInt、putString等方法以键值对(key-value)的方式保存数据,或者remove移除某个键(key),及调用clear方法删除所有内容。最后需要调用commit方法是使修改生效。
读取方式如下:
读取就更加简单了,只要获得SharedPreferences后,就可以通过getInt、getString等方法获取对应键(key)保存着的数据,如果没有找到key,则返回第二个参数作为默认值。
上面介绍SharedPreferences键值对保存的基础知识,接着我们就可以来实现Android系统的设置界面。Android系统中和设置界面相关的包为android.preference,其中有一个继承了ListActivity的PreferenceActivity。
下面我们就先实现一个如下图2的最简单的PreferenceActivity设置界面,只有一个CheckBox选择选项,选中时显示“Yes,I love you!”,取消选择时显示“No,I am sorry.”。然后再介绍其具体的实现。
图2 PreferenceActivity界面
首先新建一个工程AndroidPreferenceDemo。
把extends Activity改为extends PreferenceActivity。
在res目录下新建一个xml文件夹,接着在这个文件夹下新建一个取名为preferences.xml的File文件,内容如下:
然后把java文件中的setContentView(R.layout.main);改为
addPreferencesFromResource(R.xml.preferences);
完成了,最后可以运行试下效果。
Java文件的实现很简单,继承PreferenceActivity后,调用其public void addPreferencesFromResource (int preferencesResId)
方法从一个xml文件中获取preference然后显示为标准的设置界面。
因此我们只要在xml文件中布局好要显示的设置界面内容就可以了。
下面我们看下布局文件xml的内容。
对应的xml中可以使用的标签(Tag)可以分为两类,一类是管理布局的显示,如PreferenceScreen;另一类是具体的设置内容,如CheckBoxPreference。
PreferenceScreen可以显示一个完整的页面,可以嵌套,包含在PreferenceScreen标签里的内容都将以一个完整的页面显示。
我们学习一个PreferenceScreen的界面,如下图3所示。
图3 PreferenceScreen相关的布局
当点击左图的选项时,分别会以新的页面显示,如中间和右图所示。
上面页面对应的xml布局文件如下所示:
和管理布局显示相关的除了PreferenceScreen外,还有一个常用的为PreferenceCategory,我们把上面xml文件中间那两个PreferenceScreen改为PreferenceCategory就可以看到PreferenceCategory的显示效果了,如下图4所示。
图4 PreferenceCategory相关的布局
可以看到PreferenceCategory是把包含的内容归为同一类,但只是显示在一个页面中。
管理布局相关的两个标签可以使设置内容显示的更有条理,但是更主要的还是具体设置相关的标签,除了我们已经在使用的用于二选一的CheckBoxPreference外,Android系统提供的还有以对话框显示的DialogPreference,可以输入文本的EditTextPreference,以列表方式显示供选择的ListPreference和设置铃声用的RingtonePreference。
下面我们选择比较常用的ListPreference来介绍下具体使用。
首先在res/values/文件夹下新建一个取名为array.xml的文件,内容为:
然后在res/values/strings.xml文件中添加
需要的数据现在准备好了,下面我们来完成对应的xml文件。xml文件中各个标签的属性比较多,虽然Android有代码自动补全功能,但是还是使用Android提供的Structure界面来填写比较方便,下面我们就以这个方式来完成。
在res/xml文件夹下新建一个Android XML File文件,取名为preferencesii.xml,类型选择Preference,Root Element选择PreferenceScreen。 在Structure方式显示时,就会列出选中标签的所有属性,然后就可以根据需要填写属性,如下图5所示:
图5 填写xml中Preference的属性
从上图我们可以看到一个Preference拥有的属性。其中Key为这个Preference的ID,设置了才可以在代码中引用,Title是显示的标题,Summary是显示在标题下的文字介绍, 一般在Dependency中填写一个CheckBoxPreference的Key,这样就会在填写的那个CheckBoxPreference勾选时当前这个Preference才可用,Default Value为初始值,等等。
点击“Add”按钮,就会添加新的标签,我们依次添加一个CheckBoxPreference和ListPreference。属于CheckBoxPreference的特有属性主要为Summary On和Summary Off,比较好理解。下面具体来看下ListPreference属性的填写:
图6 ListPreference的属性
我们可以看到,ListPreference除了继承自Preference的属性外,还有自己ListPreference的属性和继承自DialogPreference的属性。其中属于ListPreference的属性有两个:Entries填的为一个字符串数组,是列表显示出来的值,而Entry Values是长度对应的字符串数组,是和Entries对应的具体的值。DialogPreference只要填一个Dialog title标题和一个取消按钮显示的字即可。在Preference属性的Dependency中我们填写上面一个CheckBoxPreference的Key,这样就会只有在CheckBoxPreference勾选时这个ListPreference才有效。
图7 完整的xml显示
最后把java文件中的addPreferencesFromResource(R.xml.preferences);改为addPreferencesFromResource(R.xml.preferencesii);
保存运行,看下效果。
图8 ListPreference显示
以上我们分别介绍了Preference对数据的保存及PreferenceActivity设置界面。当PreferenceActivity中的内容改变时,Android系统会自动进行保存和持久化维护,我们只需要在要用的设置界面中数据的地方进行读取就可以了。同时Android还提供了OnPreferenceClickListener和OnPreferenceChangeListener两个与Preference相关的监听接口,当PreferenceActivity中的某一个Preference进行了点击或者改变的操作时,都会回调接口中的函数,这样可以第一个时间向其他Activity等通知系统设置进行了改变。
下面我们以一个具体的Demo说明PreferenceActivity和其监听接口的使用。
新建一个工程AndroidPreferenceDemoII,并按上面的步骤添加xml文件夹和其内容Preferenceii.xml,还有values文件夹中的array.xml和strings.xml。
新建一个名为Settings的class,内容为:
主要是获取xml文件中的各个Preference,然后为其注册监听接口,最后在监听接口的回调函数中打印相关的信息。
接着在Manifest文件中对这个Activity进行注册:
然后对AndroidPreferenceDemoII.java文件进行如下修改:
主要是添加一个设置菜单,点击后转到设置界面,当从设置界面返回后读取保存的内容,并打印出来查看。
最后我们看一下运行的效果:
图9 设置界面操作
操作的Log输出如下图10所示:
图10 DDMS的LogCat输出
点击设置菜单转到设置界面后,首先点击CheckBox,输出前6行,然后点击ListPreference,输出3行,当选择列表的第二个进行改变时,再输出3行,最后按返回键回到第一个页面,打印出最后2行。
最后我们看一下这些设置参数在系统中的保存文件。在DDMS的File Explorer中,查看data/data/com.ichliebephone文件夹,我们可以看到有如下文件:
图11 Preference保存的文件
把这个文件导出到电脑上,可以看到起内容为:
文件以map的方式保存了两个设置参数
我们学习了Android的Preference相关内容,包括最基本的SharedPreferences的使用,及进一步的PreferenceActivity设置界面的介绍,最后根据以上的内容完成了一个简单的Demo。
Preference键值对的方式是Android最简单的一种数据持久化方式,虽然比较简单,但是也比较实用,特别是在保存小量的数据时。Android上的功能更加强大相对也更加复杂的SQLite数据保存方式我们以后接着学习。
文章对应的完整代码例子可以在这里下载:
http://download.csdn.net/source/2728956
在本文,我们将实现以下功能:
一、开机启动
很多手机软件具备开机启动的功能,有时候让软件开机启动会非常实用。那么是如何实现开机启动的呢?通过查找我们了解到开机后系统会发送一个启动完毕的广播,Intent.ACTION_BOOT_COMPLETED,那么我们只需要自定义一个BroadcastReciever接收这个广播,在接收到广播后启动我们的程序就可以实现开机启动了。
以本程序中的MyBroadcastReceiver为例:
public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { // 是否选择了开机启动 boolean start = PreferenceManager.getDefaultSharedPreferences( context).getBoolean( CleanerActivity.KEY_START_WHEN_BOOT_COMPLETED, true); if (start) { Intent i = new Intent(); i.setClass(context, FloatService.class); context.startService(i); } } } }
AndroidManifest.xml中的声明如下:
<receiver android:name="MyBroadcastReceiver" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
二、清除缓存
在Android中Settings的源码中使用了PackageManager的deleteApplicationCacheFiles()方法清除缓存,但是作为第三方软件使用这个函数难度很大,我们在PackageManager中还发现freeStorageAndNotify()方法亦可实现清除缓存,而且第三方软件使用时难度相对较小。因此,我们使用freeStorageAndNotify()方法来实现缓存的清理。
使用freeStorageAndNotify()时需要在AndroidManifest.xml中声明permission
<!-- 清除缓存时需要此权限 --> <uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
该函数的具体使用可以参考源码MyFloatView.java中的clearCache()函数。
三、杀进程
在ActivityManager.java中为我们提供了killBackgroundProcesses(String packageName)函数来杀死进程具体如何杀死进程可以参考源码MyFloatView.java中的killBackgroundProcess()函数。杀进程需要在AndroidManifest中声明如下权限
<!-- 杀死进程时需要该权限 --> <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
四、悬浮窗口的单双击
网上有很多实现悬浮窗口的教程,但是很少有人实现悬浮窗口的单击和双击事件。在本程序中我们通过添加标志位、记录用户点击悬浮窗口时的时间、单击和双击的响应使用多线程(使用Timer和TimerTask)等方式来判断单击和双击。在MyFloatView.java中的onTouchEvent()函数中对单双击进行判断。
区分单双击,是通过判断两次单击的间隔来实现的。单击事件的相应线程在执行前先进行一定时间的休眠等待判断是否为双击,根据标志位判断执行是否执行单击响应线程的函数。
五、统计RAM可用内存大小
A:Android中的MemInfoReader类通过读取/proc/meminfo实现了内存大小相关的函数,但第三方程序无法调用,我们将直接将MemInfoReader.java直接拷贝到项目中,作适当修改即可使用。
B:通过MemoryInfo和ActivityManager获得可用Ram内存大小
ActivityManager am = (ActivityManager) this .getSystemService(Context.ACTIVITY_SERVICE); MemoryInfo mi = new MemoryInfo(); am.getMemoryInfo(mi); // mi.availMem; 当前系统的可用内存 Log.e("tag", "getMemoryInfo: " + mi.availMem);
源码链接:http://pan.baidu.com/share/link?shareid=62428&uk=2953765628