概述
各种测试,各种查资料,总算解决了底部上滑锁屏功能的实现。一个底部三大金钢折腾死个人。
最后借助的还是AccessibilityService实现。因为我想实现的底部导航栏一定要显示在屏幕最底端,就算有三大金刚navigationbar,也要置于其顶层。
为了效果显示,我没有设置透明度,正常应用时,我是把背景色取消了
TYPE_ACCESSIBILITY_OVERLAY
TYPE_APPLICATION_OVERLAY
为什么不用 TYPE_APPLICATION_OVERLAY 因为底部导航栏三大金钢挡住无法接收触发事件。
正常的悬浮窗使用这个是完美的,也是官方建议的,但对于有底部导航栏和状态栏这种系统级的window在的时候,没办法,层级不够。
不多说,直接上代码解决方案
AccessibilityService
首先要继承这个类,因为TYPE_ACCESSIBILITY_OVERLAY只能在这个类里面调用,否则就会报错误 Unable to add window – token null is not valid; is your activity running
public class MyAccessibilityService extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {
}
@Override
public void onInterrupt() {
}
}
为了实现悬浮窗的绘制,再重载两个方法,开始想放在 oncreate 方法里,出了点小问题,就放在onServiceConnected里了
@Override
protected void onServiceConnected() {
initWindow();
super.onServiceConnected();
}
@Override
public void onDestroy() {
if (mView != null) windowManager.removeView(mView);
super.onDestroy();
}
具体的窗体加载方法,一些重要关键点在里面加了注释说明
@SuppressLint("ClickableViewAccessibility")
private void initWindow() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (Settings.canDrawOverlays(this)) {
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
//region 设置LayoutParams
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
layoutParams.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
} else {
layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
}
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS ;
layoutParams.format = PixelFormat.RGBA_8888; //背景透明效果
int bottomheight = 20;
layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
layoutParams.height = bottomheight;
layoutParams.gravity = 51; //想要x,y生效,一定要指定Gravity为top和left //Gravity.TOP | Gravity.LEFT
layoutParams.x = 0; //启动位置
Point point = new Point();
windowManager.getDefaultDisplay().getRealSize(point);
layoutParams.y = point.y - bottomheight;
//endregion
//加载悬浮窗布局
FloatPanelBottomBinding floatView = FloatPanelBottomBinding.inflate(LayoutInflater.from(MyAccessibilityService.this));
mView = floatView.getRoot();
//mView.setAlpha((float) 0.1);
floatView.tvPanel.setOnTouchListener(new View.OnTouchListener() {
//记录初使按下时的坐标
float startY;
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
startY = motionEvent.getRawY();
break;
case MotionEvent.ACTION_UP:
float offsetY = motionEvent.getRawY() - startY;
if (offsetY < -6) {
//锁屏
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
performGlobalAction(AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN);
}
break;
}
return true;
}
});
windowManager.addView(mView, layoutParams);
}
}
}
因为我要实现的上滑锁屏,所用我只处理了这一个功能,悬浮窗都出来,如果你想加更多功能还不是想要什么有什么?
AndroidManifest.xml
加服务,加权限,这些和正常的无障碍服务以及悬浮窗相同,本不打算提,怕有的朋友没开发过类似应用,多加个说明吧
加权限
没必要那么多,只要这一个就够了
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
加服务
<!-- 全局返回功能,锁屏功能,开启无障碍服务-->
<service android:name=".services.MyAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService"/>
</intent-filter>
<meta-data android:name="android.accessibilityservice"
android:resource="@xml/accessibilityservice"/>
</service>
accessibilityservice.xml
res / xml / accessibilityservice.xml
配置文件里面也只加个描述信息就行,多了竟给手机加负担
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/accessibility_desc"/>
Activity
这个主要是检查无障碍服务是否开启,如果没有开启就跳转过去,只能手动开启
//检查是否开启无障碍权限
public static boolean isAccessibilitySettingsOn(Context mContext, MyAccessibilityService accessibilityService) {
int accessibilityEnabled = 0;
final String service = mContext.getPackageName() + "/" + accessibilityService.getClass().getCanonicalName();
try {
accessibilityEnabled = Settings.Secure.getInt(mContext.getApplicationContext().getContentResolver(),
Settings.Secure.ACCESSIBILITY_ENABLED);
} catch (Settings.SettingNotFoundException e) {
e.printStackTrace();
}
TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');
if (accessibilityEnabled == 1) {
String settingValue = Settings.Secure.getString(mContext.getApplicationContext().getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (settingValue != null) {
mStringColonSplitter.setString(settingValue);
while (mStringColonSplitter.hasNext()) {
String accessibility = mStringColonSplitter.next();
if (accessibility.equalsIgnoreCase(service)) {
return true;
}
}
}
}
return false;
//测试时暂不做权限检查
//return true;
}
//region权限检查
if (!isAccessibilitySettingsOn(this, new MyAccessibilityService())) {
startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
}