一、为什么需要自定义锁屏页
锁屏作为一种黑白屏时代就存在的手机功能,至今仍发挥着巨大作用,特别是触屏时代的到来,锁屏的功用被发挥到了极致。多少人曾经在无聊的时候每隔几分钟划开锁屏再关上,孜孜不倦,其酸爽程度不亚于捏气泡膜。确实,一款漂亮的锁屏能为手机增色不少,但锁屏存在的核心目的主要是三个:保护自己手机的隐私,防止误操作,在不关闭系统软件的情况下节省电量。 当下,各个款式的手机自带的系统锁屏完全能够满足这些需求,而且美观程度非凡,那么开发者为什么仍然需要构建自定义锁屏呢?让我们试想一个场景,一位正在使用音乐播放器听歌的美女用户,在没有播放器自定义锁屏的情况下,切换一首歌需要几步(参考自同类文章): 1. 点亮手机屏幕 2. 解开系统锁屏 3. 打开音乐播放器 4. 切歌再熄灭屏幕 这时的她估计已经被广场舞的歌曲骚扰了有10秒,续了10次命,这是我们程序员不愿意看到的,所以有必要依靠我们灵活的双手构建出自定义的音乐锁屏页,将切歌过程被压缩为两步:点亮屏幕和切歌,顺便可以看看歌词。如果再加个开启和关闭自定义锁屏的开关,就能完美解决用户的痛点。
二、自定义锁屏页的基本原理
然而,要实现一个自定义锁屏是一件繁琐的事情,因为系统有100种方法让这个非本地的锁屏待不下去。但是,人类的智慧是无限的,程序员需要逆流而上。 Android系统实现自定义锁屏页的思路很简单,即在App启动时开启一个service,在Service中时刻监听系统SCREEN_OFF的广播,当屏幕熄灭时,Service监听到广播,开启一个锁屏页Activity在屏幕最上层显示,该Activity创建的同时会去掉系统锁屏(当然如果有密码是禁不掉的)。示意图如下:
道理很简单,我们这里需要讨论的是细节。
1. 广播注册
Service是普通的Service,在应用启动时直接startService,与应用同一个进程即可。此外,SCREEN_OFF广播监听必须是动态注册的,如果在AndroidManifest.xml中静态注册将无法接收到SCREEN_OFF广播,这点在Android官方文档中有明确说明,即需要通过如下代码注册:

2. Activity设置
锁屏的activity内部也要做相应的配置,让activity在锁屏时也能够显示,同时去掉系统锁屏。当然如果设置了系统锁屏密码,系统锁屏是没有办法去掉的,这里考虑没有设置密码的情况。 典型的去掉系统锁屏页的方法是使用KeyguardManager,具体代码如下: 

3. 屏蔽按键
当自定义锁屏页最终出现在手机上时,我们总希望它像系统锁屏页那样屹立不倒,所有的按键都不能触动它,只有通过划瓶或者指纹才能解锁,因此有必要对按键进行一定程度上的屏蔽。针对只有虚拟按键的手机,我们可以通过隐藏虚拟按键的方式部分解决这个问题,具体方法在后文会介绍。但是当用户在锁屏页底部滑动,隐藏后的虚拟按键还是会滑出,而且如果用户是物理按键的话就必须进行屏蔽了。 Back键和Menu键可以通过重写onKeyDown()方法进行屏蔽: 
4. 划屏解锁
做完以上几步,当屏幕熄灭后,再打开屏幕就能够看到我们的自定义锁屏页了,但是这时候,就算划破手指也无法解锁。所以,接下来要实现划屏解锁。 划瓶解锁的基本思路很简单,当手指在屏幕上滑动时,拦截并处理滑动事件,使锁屏页面随着手指运动,当运动到达一定的阀值时,用户手指松开手指,锁屏页自动滑动到屏幕边界消失,如果没有达到运动阀值,就会自动滑动到起始位置,重新覆盖屏幕。 为了将划屏逻辑与页面内容隔离开来,我们在锁屏页面布局中添加一个自定义的UnderView,这个UnderView填充整个屏幕,位于锁屏内容View(将其引用称之为mMoveView,并传入到UnderView中)的下方,所有划屏相关的事件都在这里拦截并处理。 



三、透明栏与沉浸模式
沉浸模式与透明栏是两个不同的概念,由于某些原因,国内一些开发或产品会把这两个概念混淆。不过没关系,在接下来的内容我们会对这两个概念进行详细的解释和区分,并应用这两种不同的模式进一步完善已经初具模样的锁屏页。
1. 沉浸模式
什么是沉浸模式?从4.4开始,Android 为 “setSystemUiVisibility()”方法提供了新的标记 “SYSTEM_UI_FLAG_IMMERSIVE”以及”SYSTEM_UI_FLAG_IMMERSIVE_STIKY”,就是我们所谈的沉浸模式,全称为 “Immersive Full-Screen Mode”,它可以使你的app隐藏状态栏和导航栏,实现真正意义上的全屏体验。 之前 Android 也是有全屏模式的,主要通过”setSystemUiVisibility()”添加两个Flag,即”SYSTEM_UI_FLAG_FULLSCREEN”,”SYSTEM_UI_FLAG_HIDE_NAVIGATION”(仅适用于使用导航栏的设备,即虚拟按键)。 这两个标记都存在一些问题,例如使用第一个标记的时候,除非 App 提供暂时退出全屏模式的功能(例如部分电子书软件中点击一次屏幕中央位置),用户是一直都没法看见状态栏的。这样,如果用户想去看看通知中心有什么通知,那就必须点击一次屏幕,显示状态栏,然后才能调出通知中心。 而第二个标记的问题在于,Google 认为导航栏对于用户来说是十分重要的,所以只会短暂隐藏导航栏。一旦用户做其他操作,例如点击一次屏幕,导航栏就会马上被重新调出。这样的设定对于看图软件,视频软件等等没什么大问题,但是对于游戏之类用户需要经常点击屏幕的 App,那就几乎是悲剧了——这也是为什么你在 Android 4.4 之前找不到什么全屏模式会自动隐藏导航栏的应用。 Android 4.4 之后加入的Immersive Full-Screen Mode 允许用户在应用全屏的情况下,通过在原有的状态栏/导航栏区域内做向内滑动的手势来实现短暂调出状态栏和导航栏的操作,且不会影响应用的正常全屏,短暂调出的状态栏和导航栏会呈半透明状态,并且在一段时间内或者用户与应用内元素进行互动的情况下自动隐藏,沉浸模式的四种状态如下图。(参考www.jcodecraeer.com/a/anzhuokai…)
状态1代表没有进入沉浸模式时页面的状态,仍然可以看到Status Bar和Navigation Bar;状态2代表用户第一次进入沉浸模式时,系统的提示弹窗,告诉用户如何在沉浸模式下呼出Status Bar和Navigation Bar;状态3代表沉浸模式,可以看到Status Bar和Navigation Bar都被隐藏;状态4代表用户在Sticky沉浸模式下呼出Status Bar和Navigation Bar,可以看到两个Bar重新出现,但是过一段时间能够自动隐藏。 一般来说,沉浸模式的标记与其他Full Screen相关的Flag搭配起来才能达到我们想要的效果,即通过沉浸模式标记规定状态栏status bar和导航栏navigation bar显示和隐藏的运转逻辑,通过其他标签设定状态栏和导航栏显示或隐藏,以及显示或隐藏的样子。这些常见的Flag及相应功能如下表:




2. 透明栏
什么是透明栏?Google 在 Android 4.4 的 API 描述页面里提到了“Translucent system UI styling”,即半透明化的系统UI风格。这个“半透明化”包括了状态栏和通知栏,当开发者让应用支持这个新特性的时候,状态栏和导航栏可以单独/同时变为渐变的半透明样式,如下图:





四、指纹解锁
到这里,我们的锁屏页已经基本完工,完全能够非常优雅地解决用户的痛点,但是跟当下App自定义锁屏页的区别并不明显。接下来对新型号手机普遍具备的指纹解锁功能的考虑,则能够为锁屏页增色不少。
1. 指纹识别无法解锁自定义锁屏页的问题
持有指纹解锁手机的用户在使用App自定义锁屏页时会出现一种困惑,当你点亮屏幕,能够看到自定义锁屏页,在使用指纹解锁成功之后(部分机型指纹解锁操作只能在系统锁屏页进行),自定义锁屏页依然存在,你还是需要划开自定义锁屏页,才能看到手机主界面。 解决这一问题的方案是一种取巧的方法,那就是在锁屏页的service中监听ACTION_USER_PRESENT广播。ACTION_USER_PRESENT广播是系统锁屏解锁广播,当系统锁屏页解锁时就会触发。如果在接收到这一广播时,将自定义锁屏页finish掉,就能避免在指纹解锁成功后自定义锁屏页仍然显示的问题。但是细心的读者会发现这种解法在逻辑上还存在问题,因为在用户没有设置锁屏密码的情况下,前文自定义锁屏页在onCreate()时设置的FLAG_DISMISS_KEYGUARD标志位能够轻易解锁系统的锁屏页,并触发ACTION_USER_PRESENT广播,此时自定义锁屏页的Service接收到这一广播后,发finish广播给自定义锁屏页,导致自定义锁屏页刚create就finish掉了,永远不可能出现。 因此,我们必须对场景进行区分,只在有锁屏密码的情况下,才对接收到的ACTION_USER_PRESENT广播进行处理,finish自定义锁屏页。即在BroadcastReceiver的onReceive()方法中加入如下代码: 
2. 自定义锁屏页下指纹识别无法使用的问题
此外,有些手机型号,比如小米,在自定义锁屏页罩在系统锁屏页之上时(设置有锁屏密码),指纹解锁是无效的,也就是必须要划开自定义锁屏页,在系统锁屏页上才能进行指纹解锁。为了改善这种体验,我们可以在Activity中引入指纹解锁API,识别指纹并解锁,具体代码如下:


五、总结
通过以上内容的分享,本鹅希望能够对大家的开发有所帮助,如果内容有问题,也希望大家指点。综上所述,在Android上实现自定义锁屏页并不是一件复杂的事情,关键是对一些技术点的把握要比较清楚。Service中启动Activity的正确方法,广播静态注册与动态注册的差别,touch事件的分发传播机制,透明栏与沉浸模式的综合运用,以及指纹识别新技术的应用,都有很多值得推敲的地方。笔者当初实现自定义锁屏页时,没有太多思考,有时照搬前人的做法,有时各种flag随便添加,有时新旧API混淆,虽然实现了需求,但是代码不够简洁,可读性也差。因此,在今后的开发过程中,除了要快速实现需求,还要在随后的维护中,多多思考和研究,使代码能够达到“少一行不行,多一行难受”的境界。
今天的文章浅谈Android自定义锁屏页的发车姿势分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/23160.html