Android屏幕锁屏弹窗的正确姿势DEMO详解
在上篇⽂章给⼤家介绍了。今天通过本⽂给⼤家分享android锁屏弹窗的正确姿势。
最近在做⼀个关于屏幕锁屏悬浮窗的功能,于是在⽹上搜索了很多安卓屏幕锁屏的相关资料,鉴于⽹上的资料⽐较零碎,所以我在这⾥进⾏整理总结。本⽂将从以下两点对屏幕锁屏进⾏解析:
1. 如何监听系统屏幕锁屏
2. 如何在锁屏界⾯弹出悬浮窗
如何监听系统屏幕锁屏
经过总结,监听系统的锁屏可以通过以下两种⽅式:
1) 代码直接判定
2) 接收⼴播
1) 代码直接判定
代码判断⽅式,也有两种⽅法:
a) 通过PowerManager的isScreenOn⽅法,代码如下:
PowerManager pm = (PowerManager)
//如果为true,则表⽰屏幕“亮”了,否则屏幕“暗”了。
boolean isScreenOn = pm.isScreenOn();
这⾥需要解释⼀下:
屏幕“亮”,表⽰有两种状态:a、未锁屏 b、⽬前正处于解锁状态。这两种状态屏幕都是亮的;
屏幕“暗”,表⽰⽬前屏幕是⿊的。
b) 通过KeyguardManager的inKeyguardRestrictedInputMode⽅法,代码如下:
KeyguardManager mKeyguardManager = (KeyguardManager) SystemService(Context.KEYGUARD_SERVICE);
boolean flag = mKeyguardManager.inKeyguardRestrictedInputMode();
对flag进⾏⼀下说明,经过试验,总结为:
如果flag为true,表⽰有两种状态:a、屏幕是⿊的 b、⽬前正处于锁屏状态。
如果flag为false,表⽰⽬前未锁屏
注明:上⾯的两种⽅法,也可以通过反射机制来调⽤。
反射代码如下:
private static Method mReflectScreenState;
try {
mReflectScreenState = Method(isScreenOn, new Class[] {});
PowerManager pm = (PowerManager) SystemService(Activity.POWER_SERVICE);
boolean isScreenOn= (Boolean) mReflectScreenState.invoke(pm);
} catch (Exception e) {
e.printStackTrace()
}
2) 接收⼴播
当安卓系统锁屏或者屏幕亮起,或是屏幕解锁的时候,系统内部都会发送相应的⼴播,我们只需要对⼴播进⾏监听就可以了注册⼴播的伪代码如下:
private ScreenBroadcastReceiver mScreenReceiver;
private class ScreenBroadcastReceiver extends BroadcastReceiver {
private String action = null;
@Override
public void onReceive(Context context, Intent intent) {
action = Action();
if (Intent.ACTION_SCREEN_ON.equals(action)) {
// 开屏
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
// 锁屏
} else if (Intent.ACTION_USER_PRESENT.equals(action)) {
// 解锁
}
}
}
private void startScreenBroadcastReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);
}
如何在锁屏界⾯弹出悬浮窗
竟然知道了对于系统屏幕监听的⽅法,那么接下来就是要在屏幕锁屏的时候,弹出悬浮框了,这个的实现⽅式有两种:
1) 使⽤WindowManager
2) 使⽤Activity
⽬前情况是,使⽤这两种⽅式在真机上都可以实现,如果⽹友们发现有问题,可以在博客中留⾔
1) 使⽤WindowManager
代码如下:
private void init(Context mContext) {
this.mContext = mContext;
mWindowManager = (WindowManager) SystemService(Context.WINDOW_SERVICE);
// 更新浮动窗⼝位置参数靠边
DisplayMetrics dm = new DisplayMetrics();
// 获取屏幕信息
mScreenWidth = dm.widthPixels;
mScreenHeight = dm.heightPixels;
this.mWmParams = new WindowManager.LayoutParams();
// 设置window type
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
} else {
}
// 设置图⽚格式,效果为背景透明
mWmParams.format = PixelFormat.RGBA_8888;
// 设置浮动窗⼝不可聚焦(实现操作除浮动窗⼝外的其他可见窗⼝的操作)
mWmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
// 调整悬浮窗显⽰的停靠位置为左侧置�?
mScreenHeight = DefaultDisplay().getHeight();
// 以屏幕左上⾓为原点,设置x、y初始值,相对于gravity
mWmParams.x = 0;
mWmParams.y = mScreenHeight / 2;
// 设置悬浮窗⼝长宽数据
mWmParams.width = LayoutParams.WRAP_CONTENT;
mWmParams.height = LayoutParams.WRAP_CONTENT;
addView(createView(mContext));
mWindowManager.addView(this, mWmParams);
mTimer = new Timer();
hide();
}
WindowManager的主要配置就是上⾯的那些代码,这⾥需要说明⼀下,type的类型有如下值:
应⽤程序窗⼝。
public static final int FIRST_APPLICATION_WINDOW = 1;
所有程序窗⼝的“基地”窗⼝,其他应⽤程序窗⼝都显⽰在它上⾯。
public static final int TYPE_BASE_APPLICATION =1;
普通应⽤功能程序窗⼝。token必须设置为Activity的token,以指出该窗⼝属谁。
public static final int TYPE_APPLICATION = 2;
⽤于应⽤程序启动时所显⽰的窗⼝。应⽤本⾝不要使⽤这种类型。
它⽤于让系统显⽰些信息,直到应⽤程序可以开启⾃⼰的窗⼝。
public static final int TYPE_APPLICATION_STARTING = 3;
应⽤程序窗⼝结束。
public static final int LAST_APPLICATION_WINDOW = 99;
⼦窗⼝。⼦窗⼝的Z序和坐标空间都依赖于他们的宿主窗⼝。
public static final int FIRST_SUB_WINDOW = 1000;
⾯板窗⼝,显⽰于宿主窗⼝上层。
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
媒体窗⼝,例如视频。显⽰于宿主窗⼝下层。
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1;
应⽤程序窗⼝的⼦⾯板。显⽰于所有⾯板窗⼝的上层。(GUI的⼀般规律,越“⼦”越靠上)
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW +2;
对话框。类似于⾯板窗⼝,绘制类似于顶层窗⼝,⽽不是宿主的⼦窗⼝。
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW +3;
媒体信息。显⽰在媒体层和程序窗⼝之间,需要实现透明(半透明)效果。(例如显⽰字幕)
public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW +4;
⼦窗⼝结束。( End of types of sub-windows )
public static final int LAST_SUB_WINDOW = 1999;
系统窗⼝。⾮应⽤程序创建。
public static final int FIRST_SYSTEM_WINDOW = 2000;
状态栏。只能有⼀个状态栏;它位于屏幕顶端,其他窗⼝都位于它下⽅。
public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;
搜索栏。只能有⼀个搜索栏;它位于屏幕上⽅。
public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;
电话窗⼝。它⽤于电话交互(特别是呼⼊)。它置于所有应⽤程序之上,状态栏之下。
public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;
系统提⽰。它总是出现在应⽤程序窗⼝之上。
public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW +3;
锁屏窗⼝。
public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW +4;
信息窗⼝。⽤于显⽰toast。
public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW +5;
系统顶层窗⼝。显⽰在其他⼀切内容之上。此窗⼝不能获得输⼊焦点,否则影响锁屏。
public static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW +6;
电话优先,当锁屏时显⽰。此窗⼝不能获得输⼊焦点,否则影响锁屏。
public static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW +7;
系统对话框。(例如⾳量调节框)。
public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW +8;
锁屏时显⽰的对话框。
public static final int TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW +9;
系统内部错误提⽰,显⽰于所有内容之上。
public static final int TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW +10;
内部输⼊法窗⼝,显⽰于普通UI之上。应⽤程序可重新布局以免被此窗⼝覆盖。
public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW +11;
内部输⼊法对话框,显⽰于当前输⼊法窗⼝之上。
public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW +12;
墙纸窗⼝。
public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW +13;
状态栏的滑动⾯板。
public static final int TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW +14;
系统窗⼝结束。
public static final int LAST_SYSTEM_WINDOW = 2999;
如果想让悬浮窗在所以锁屏之上,使⽤TYPE_SYSTEM_ERROR,因为它显⽰在所有内容之上。
2) 使⽤Activity
Activity的设置
Activity需要进⾏以下设置,才可以在锁屏状态下弹窗。
⾸先是onCreate⽅法,需要添加4个标志,如下:
protected void onCreate(Bundle savedInstanceState) {
final Window win = getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
// ⾃⼰的代码
}
四个标志位顾名思义,分别是锁屏状态下显⽰,解锁,保持屏幕长亮,打开屏幕。这样当Activity启动的时候,它会解锁并亮屏显⽰。
然后在l⽂件当中,对该activity的声明需要加上以下属性:
<activity android:name=".alarm.AlarmHandlerActivity"
android:launchMode="singleInstance"
android:excludeFromRecents="true"
android:taskAffinity=""
android:theme="@android:style/Theme.Wallpaper.NoTitleBar"/>
⽽对于布局⽂件,要显⽰的view居中,背景透明。由于上⾯已经设置了背景为壁纸的背景,所以显⽰的是桌⾯的背景。如果背景设为默认的⽩⾊,则导致弹窗后⾯是⼀⽚⽩⾊,看起来很丑。如果背景设置为透明,则弹窗后⾯会显⽰出解锁后的界⾯(即使有锁屏密码,也是会显⽰解锁后的界⾯的),⼀样很影响视觉效果。
在⼴播中启动锁屏弹窗
我们设置的是锁屏下才弹窗的,⾮锁屏下就不适合弹出这个窗⼝了(你可以试⼀下,效果会很怪)。⼀般是注册⼀个⼴播接收器,在接收到指定⼴播之后判断是否需要弹窗,所以在BroadcastReceiver的接收代码中需要先判断是否为锁屏状态下:
@Override
public void onReceive(Context context, Intent intent) {
Log.d(LOG_TAG, Action());
KeyguardManager km = (KeyguardManager) SystemService(Context.KEYGUARD_SERVICE);
if (km.inKeyguardRestrictedInputMode()) {
Intent alarmIntent = new Intent(context, AlarmActivity.class);
alarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(alarmIntent);
}
}
这⾥⽤到的是KeyguardManager类,⽤来管理锁屏的,4.1之后该类的API新增了⼀个isKeyguardLocked()的⽅法判断是否锁屏,但在4.1之前,我们只能⽤inKeyguardRestrictedInputMode()⽅法,如果为true,即为锁屏状态。需要注意的是,在⼴播中启动Activity的context可能不是Activity对象,所以需要添加NEW_TASK的标志,否则启动时可能会报错。我们就可以结合之前的系统发送⼴播后进⾏相应的悬浮窗的弹出处理。
复写onNewIntent⽅法
再次亮起屏幕,如果该Activity并未退出,但是被⼿动按了锁屏键,当前⾯的⼴播接收器再次去启动它的时候,屏幕并不会被唤起,所以我们需要在activity当中添加唤醒屏幕的代码,这⾥⽤的是电源锁。可以添加在onNewIntent(Intent intent),因为它会被调⽤。也可以添加在其他合适的⽣命周期⽅法。添加代码如下:
PowerManager pm = (PowerManager) SystemService(Context.POWER_SERVICE);
if (!pm.isScreenOn()) {
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP |
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "bright");
自动弹窗代码wl.acquire();
}
最后,是添加如下权限
<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
第⼀条是解锁屏幕需要的,第⼆条是申请电源锁需要的。
以上所说是⼩编给⼤家介绍的Android屏幕锁屏弹窗的正确姿势DEMO详解,希望对⼤家有所帮助,如果⼤家有任何疑问欢迎给我留⾔,⼩编会及时回复⼤家的,在此也⾮常感谢⼤家对⽹站的⽀持!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论