不知不觉已经完成了最后一个功能,其实这个功能对我这个小白来说还是有些棘手的。期间遇到了很多困难,但经过不懈的努力,大量的查找资料,以及林老师的技术支持,最后的结果还是可喜的。
最后的任务是要给seadrod加个软件锁,但是由于安全性以及用户体验,我决定使用手势锁。因为安卓的开源性,手势锁的核心代码有源码的参考,网上的资料很齐全并且很相似,反而没有占用大量的时间。相反,我在设置界面(SettingsActivity)大费周章。
一、数据的保存与读取
安卓专门为settings提供了一种存储方式——SharedPreferences。这样可以将一些需要全局使用的变量放入XML文件中,这样既能保证数据的安全性,同时又可以在整个程序中使用。有三种获取系统中保存的持久化数据的方式:
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后就可以对数据进行读取或者保存了。
保存的方法:
String STORE_NAME = "Settings";
SharedPreferences settings = getSharedPreferences(STORE_NAME, MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putInt("sourceType", 0);
editor.commit();
读取的方法:
SharedPreferences settings = getSharedPreferences(STORE_NAME, MODE_PRIVATE);
int source = settings.getInt("sorceType", 1);
二、界面的设计
前几日我在网上搜索Preferences相关资料时,大多数的前辈使用PreferencesActivity以及它其中的Preferences控件来制作设置界面。但当我extends PreferenceActivity时Eclipse一条删除线划掉了,说明PreferenceActivity已经被谷歌舍弃。幸运的是谷歌用PreferenceFragment来取代PreferenceActivity。他们的区别只有在最开始的绘制界面,Preferences控件基本上没有改变,在Fragment里也可以使用getActivity()来调用Activity的控件。
除了要新建一个类extends PreferenceFragment外,还要新建一个Activity来装载这个Fragment。装载办法就是在Activity的onCreat()函数中运行如下代码:
setContentView(R.layout.settings_activity_layout);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.settings_fragment_container, new SettingsPreferenceFragment());
fragmentTransaction.commit();
在绘制界面上Preference与其他不同的在于,他的界面并不能放在res/layout里,要放在res/xml文件夹下,并通过R文件来绘制出界面,具体绘制函数也有所不同:
addPreferencesFromResource(R.xml.settings);
控制Preference控件的函数有三个:Preference.OnPreferenceChangeListener、Preference.OnPreferenceClickListener以及onPreferenceTreeClick。
它的触发规则如下:
1 先调用onPreferenceClick()方法,如果该方法返回true,则不再调用onPreferenceTreeClick方法 ;
如果onPreferenceClick方法返回false,则继续调用onPreferenceTreeClick方法。
2 onPreferenceChange的方法独立与其他两种方法的运行,如果返回true则接受控件值的改变,返回false则不接受。也就是说,它总是会运行。
继续回到我们的设置界面。我们要达到的目标是:当Checkbox不为对勾时,用户点击,触发手势密码的设置界面(即跳转Activity),设置界面结束时,会有两种状态,用户设置成功或是设置失败。若是设置成功,Checkbox变为对勾状,否则不改变。当checkbox是对勾状时,用户点击,对勾取消,并且清除SharePreference中的密码。
根据需求来看,我们必定要在onPreferenceClick中加入startActivityForResult(); 并且需要得到用户是否将手势密码设置成功(setupSuccess),于是我们要重写onActivityResult:
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case Gesture_Lock_REQUEST:
if (resultCode == getActivity().RESULT_OK) {
setupSuccess = data.getBooleanExtra("setupSuccess", true);
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getActivity());
SharedPreferences.Editor editor = settings.edit();
if (setupSuccess == true) {
showToast(R.string.setup_gesture_lock_success);
editor.putBoolean(BrowserActivity.GESTURE_LOCK_SWITCH_KEY, true);
gestureLockSwitch.setChecked(true);
} else {
editor.putBoolean(BrowserActivity.GESTURE_LOCK_SWITCH_KEY, false);
gestureLockSwitch.setChecked(false);
}
editor.commit();
}
}
}
网上对于checkbox的控制基本上都是使用onPreferenceChange,因为它可塑性很高,很明显它无法满足我们的要求。通过请教林老师,我们可以用另一种办法绕开这种机制:首先将onPreferenceChange设为始终返回false,屏蔽它,然后Checkbox.setChecked()来手动控制checkbox的值,这样便可以达到我们的要求。
部分代码如下:
if (setupSuccess == true) {
showToast(R.string.setup_gesture_lock_success);
editor.putBoolean(BrowserActivity.GESTURE_LOCK_SWITCH_KEY, true);
gestureLockSwitch.setChecked(true);
} else {
editor.putBoolean(BrowserActivity.GESTURE_LOCK_SWITCH_KEY, false);
gestureLockSwitch.setChecked(false);
}
三、手势密码
正如上文所说,手势密码的核心代码在安卓源程序中可以参考。手势密码锁的Activity原理很简单,就是根据手势密码的核心代码绘制出手势密码(需要引用核心代码中的一些函数和接口),作为Activity弹出,当用户绘制完成后进行比对,若一致,则finish(),若不一致则弹出提示。具体代码这里并不多述,可去我的CODE中查看。
对于手势密码的设置:
界面上除了绘制密码的区域,还需要增加两个按钮:取消\重试按钮 和 继续\完成按钮。 大致思路是,用户第一次绘制完成时可以选择取消,也可以继续第二次绘制确认。只有当两次绘制的图形一样时才算设置成功,在第二次绘制时也可选择重试,重新绘制第一次的图案。
因为这个思路我们大致分为四步:
第一步为等待用户设置手势图案,将界面绘制,并设置取消键和继续键,这时继续键不可用。
第二步为用户第一次绘制完成,继续键可用,用户可以选择取消或是继续。
第三步为等待用户设置第二次的手势图案,设置重试和完成键,这时完成键不可用。
第四步为用户第二次绘制完成,两次设置的图案相同时完成按钮变为可用,点击后将密码转为字串保存。
具体实现代码很长,全贴出来并不方便,可进入我的CODE查看
至于如何在程序开始时弹出手势锁,其实只是在程序主界面生成前弹出一个Activity覆盖在最前,手势密码输入正确finish()便可。代码如下(要放在MainActivity的onCreate函数中):
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
String lockPattenString = settings.getString(LOCK_KEY, null);
if (lockPattenString != null) {
Intent intent = new Intent(this, GestureLockActivity.class);
startActivity(intent);
}
至此,我们的任务就全部完成了!