我实现的效果css
Android自4.0版本, 也就是API level 14开始, 加入了锁屏控制的功能, 相关的类是RemoteControlClient
, 这个类在API level 21中被标记为deprecated, 被新的类MediaSession
所替代. 咱们的音乐App中最开始使用的是原生锁屏控制API, 说实话这个API很差用, 遇到了一些小坑, 最要命的是不一样品牌的手机, 锁屏界面长的还不同, 就连我本身都没见过原生4.0的锁屏控制界面是什么样的. 国内的手机厂商都自觉得本身的审美很强, 设计了千奇百怪的锁屏控制界面, MIUI更奇怪, MIUI 6是在原生4.4.4的基础上改的, 居然有一段时间都没有锁屏控制界面, 后来更新才有. 而原生Android在5.0时, 将锁屏和通知栏控制合并, 整个逻辑很是混乱. 咱们仍是决定像QQ音乐/酷狗音乐那样, 本身作一个锁屏控制页面java
题外话: 给RemoteControlClient设置封面时, 参数是一个Bitmap, 这个参数传入后, 千万不要在其余地方使用这个Bitmap, 也不要持有它的引用, 更不要自做聪明调用它的recycle方法.android
首先想到的因该是作一个锁屏, 也就是使用Android的API, 作一个锁屏应用, 和输入法等应用同样, 但这个方法成本很高. 国内的那些锁屏应用, 首先要作的就是引导用户设置锁屏应用, 步骤至关繁杂, 只是为了一个播放控制就用一个锁屏应用, 没有哪一个用户会这么有耐心.程序员
得益于我国程序员的脑洞, 咱们有了第二种思路: 悬浮窗.
悬浮窗的一个比较严谨的名字叫系统警告窗口, 国内外的一些流氓厂商, 常常用悬浮窗弹一些广告, 这个悬浮窗是浮在正常的app的上面的, 因此若是它不消失, 极可能你连正常使用手机都有问题.
这是一个比较打扰用户的东西, 并且也有必定的安全风险. MIUI的权限管理默认是将悬浮窗关闭的, 而有道词典的复制查词功能, 就是用悬浮窗作的, 若是你没给有道词典打开这个权限, 复制查词这个功能就废了.安全
文章开头的GIF图片展现的效果, 就是用一个普通Activity作的.
国内的app们, 最终都选择了这条道路, 不知道他们是谁抄的谁, 第一个想到使用普通Activity伪造一个锁屏的开发者, 我只能说很是有创造力.app
准确来讲咱们监听的是屏幕熄灭事件, 关屏事件的Intent是Intent.ACTION_SCREEN_OFF
, 不须要任何权限就能够监听, 可是必须使用代码注册, 也就是说咱们必须有一个Service在后台监听才行, 对音乐类app来讲, 这不是问题, 音乐app自己就是使用Service来控制MediaPlayer
的. 只须要在Service中注册监听Intent.ACTION_SCREEN_OFF
就行. 监听到这个事件, 咱们就启动一个Activity, 这就是咱们的锁屏Activity.ide
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_SCREEN_OFF)) {
Intent lockscreen = new Intent(PlaybackService.this, LockScreenActivity.class);
lockscreen.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(lockscreen);
}
}
注意咱们在Service中启动一个Activity, 须要加上Intent.FLAG_ACTIVITY_NEW_TASK
这个flag才行.测试
要想作到锁屏那样滑动解锁, 好比像图中的样子, 咱们除了要根据手势移动View之外, 还要让Activity的背景透明, 好比将theme设置成下面这样.动画
<style name="LockScreenBase" parent="AppBaseTheme"> <item name="android:windowIsTranslucent">true</item> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:colorBackgroundCacheHint">@null</item> <item name="android:windowNoTitle">true</item> <item name="android:backgroundDimEnabled">false</item> <item name="android:windowAnimationStyle">@null</item> <item name="android:windowContentOverlay">@null</item> </style>
这个style其实作了不少东西, 你们根据本身的须要能够删减一部分, 好比状态栏透明, 不使用TitleBar之类的.this
在显示咱们的假锁屏的时候, 咱们应当帮用户解锁, 这样咱们才能冒充锁屏, 而不会出现用户”解锁”两次的状况, 但咱们只能要求系统解锁没有密码的锁屏, 有密码的状况下, 咱们是不能解锁屏幕的, 这时咱们应该覆盖在锁屏界面上, 幸亏, 在API level 5中就引入了两个Flag,FLAG_DISMISS_KEYGUARD
和FLAG_SHOW_WHEN_LOCKED
在锁屏Activity的onCreate
方法中给Activity加上两个Flag
this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
必定要两个一块儿用, 不然效果不大好, 当时测试了很久, 后来看了一下QQ音乐的实现, 才发现两个一块儿用效果才好, 不然会有一些奇怪的问题.
做为一个锁屏界面, 应当是独立的, 也就是说, 咱们这个Activity应当独立于咱们的App存在, 至少看起来是这样. 从Android的角度来看, 咱们app的主界面里的全部Activity, 应当在一个Task里, 而锁屏Activity, 应当在一个独立的Task里, 所以咱们须要给锁屏Activity一个独立的Task, 并且不管什么时候, 都只有一个锁屏Activity实例存在.
另外, Android有查看近期任务的功能, 咱们不但愿锁屏界面这个独立的Task显示在里面, 因此锁屏Activity不能显示在近期任务中.
说了这么多, 要作很简单, 只须要在Manifest里面声明Activity时加入几个属性便可
<activity android:excludeFromRecents="true" android:exported="false" android:launchMode="singleInstance" android:name=".view.lockscreen.LockScreenActivity" android:screenOrientation="portrait" android:taskAffinity="com.package.name.lockscreen" android:theme="@style/LockScreenTheme">
</activity>
上面的属性中android:excludeFromRecents="true"
让锁屏Activity不显示在近期任务中,android:launchMode="singleInstance"
和android:taskAffinity="com.package.name.lockscreen"
保证锁屏Activity有一个单独的Task, 且这个Task里永远只有它一个实例.
锁屏界面固然不响应Back按键, 只须要重写Activity的onBackPressed
方法便可
@Override
public void onBackPressed() {
// do nothing
}
咱们没法监听Home按键, 可是能够改变因Home进入后台时的处理, 好比在Manifest的activity声明中加上
android:noHistory="true"
这样若是用户经过Home按键让咱们的应用进入后台, 咱们会让这个activity销毁, 就像咱们被滑动关闭同样.
若是不加, 最好重写Activity的onNewIntent
来应对因Home进入后台, 而后Service再次启动锁屏Activity的状况.
咱们的锁屏Activity在滑动”解锁”以后, 理论上是直接进入下面的界面, 但有时若是下面不是launcher, 而是一个app, 有可能会闪一下黑屏, 这个实际上是底下activity的入场动画致使的, 某些Android版本会对顶部activity透明时处理有些奇怪, 咱们不能保证其余的应用不闪黑屏, 可是对本身的的应用仍是能够的, 只须要在咱们的主体activity的style中加上
<item name="android:windowAnimationStyle">@null</item>
就不会有这种状况发生了, 可是这样的话入场动画也没了, 总之如何取舍看你们了.