一、引言
1、软件项目概述
基于用户使用app过程中,手机屏幕关闭后,重新点亮手机屏幕,能够快速看见用户的所需信息的需求,来设计app的自定义锁屏效果。
2、概述
本文档就自定义锁屏的实现进行讲解。
二、自定义锁屏的实现方案
1、方案一
1)、总体实现
在app需要时开启一个service,在service中时刻监听系统息屏(SCREEN_OFF)广播,当屏幕熄灭时,Service监听到广播,开启锁屏页Activity,在屏幕的最上层显示。并在activity创建时隐藏掉系统锁屏(有密码的情况下是禁止不掉的)。
2)、详细实现
(1)、开启服务注册广播
//开启服务
startService(this,LockScreenService.class);
//注册屏幕熄灭广播
IntentFiltermScreenOffFilter = new IntentFilter();
mScreenOffFilter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(mScreenOffReceiver, mScreenOffFilter);
(2)、监听广播开启锁屏
if(intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Intent mLockIntent = new Intent(context, LockScreenActivity.class);
mLockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK );
startActivity(mLockIntent);
}
注意:将锁屏Activity的启动模式设置为singleTask,否则锁屏Activity实质上还是建立在原来的App的task栈中(可以根据需求做修改)。
(3)、去掉系统锁屏
在启动自定义锁屏的同时,去掉系统锁屏。当然如果设置了系统锁屏密码,系统锁屏是无法去掉的,具体代码如下:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
FLAG_DISMISS_KEYGUARD用于去掉系统锁屏页。
FLAG_SHOW_WHEN_LOCKED使Activity在锁屏时能够显示。
去掉系统锁屏需要添加权限如下:
<uses-permissionandroid:name="android.permission.DISABLE_KEYGUARD" />
(4)、屏蔽Back和Menu键
重写onKeyDown方法屏蔽,代码如下:
public boolean onKeyDown(int keyCode,KeyEvent event) {
int key = event.getKeyCode();
switch (key) {
case KeyEvent.KEYCODE_BACK:
return true;
case KeyEvent.KEYCODE_MENU:
return true;
default:
break;
}
return super.onKeyDown(keyCode,event);
}
当熄灭屏幕后,就可以看到我们的自定义锁屏页了。
(5)、滑屏解锁
当手指在屏幕上滑动时,拦截并处理滑动事件,使锁屏页随着手指运动,当超过一定距离,锁屏消失,否则重新覆盖屏幕。
注意:需要给锁屏Activity设置透明主题,否则滑动看不见背景页面。
关键代码如下:
publicboolean onTouchEvent(MotionEvent event) {
mWidth = getWidth();
int action = event.getAction();
float nx = event.getX();
switch (action) {
case MotionEvent.ACTION_DOWN:
mStartX = nx;
break;
case MotionEvent.ACTION_MOVE:
handleMoveView(nx);
break;
case MotionEvent.ACTION_UP:
caseMotionEvent.ACTION_CANCEL:
doMoveEvent(nx);
break;
}
return true;
}
mStartX:用来记录滑动的起始位置。
handleMoveView():根据滑动的x轴距离,来控制view的移动。
doMoveEvent():根据结束位置和起始位置的距离,来判断是否超过阈值,如果超过了阈值,view滑出屏幕,销毁锁屏;否则重新覆盖屏幕
(6)、近期任务不显示锁屏Activity
android:excludeFromRecents=”true”,让锁屏Activity不显示在近期任务中。
3)、结论
这套方案完成锁屏页的实现,可以满足用户方便查看运动状态的需求。
2、方案二
1)、总体实现
同方案一。
2)、详细实现
(1)步骤
详细实现步骤同方案一,只是加载布局的方式不同。
(2)加载布局
方案一调用Activity的setContentView()方法加载布局;
方案二将布局inflate()成View后设成系统错误的view。
原理:是把这个view当成系统的错误的view,sdk文档解释说明。
Window type: internal system error windows, appear on top of everything theycan. In multiuser systems shows only on the owning user's window.
关键代码如下:
mWindowManager = (WindowManager)mContext.getApplicationContext().getSystemService("window");
mParams = new LayoutParams();
mParams.type = LayoutParams.TYPE_SYSTEM_ERROR;
mWindowManager.addView(mLockView, mParams);
3)、结论
这个error的view在所有的view的最上面,可以达到屏蔽home键的,Recent键以及下拉效果,并且长按power键,关机界面也弹不出来。这套方案同样可以满足用户方便查看详细信息的需求。
三、问题与总结
1、禁止Home键与Recent键
Home键与Recent键(调出最近打开应用的按键)的点击事件是在Framework层进行处理的,因此onKeyDown与dispatchKeyEvent都捕获不到点击事件,所以无法屏蔽其点击效果。关于这两个按键的屏蔽方法,网上用到了反射,有的是通过改变Window的标志位和Type等,总的来说,这些方法只对部分Android版本有效,有的则无法编译通过。所以无法在应用层屏蔽掉Home键以及Recent键。
2、总结
方案二虽然屏蔽掉了按键的响应,但也一定程度上影响了用户体验,例如:关机操作。所以不建议使用。
方案一在满足用户的需求的同时,并没有影响到用户的体验。并且与市场上qq音乐,keep的锁屏页实现了相同的效果(并没有对home键,Recent键以及下拉菜单做相应的处理),所以方案一比较合适。