android底部上滑锁屏

概述 各种测试,各种查资料,总算解决了底部上滑锁屏功能的实现。一个底部三大金钢折腾死个人。 最后借助的还是AccessibilityService实现。因为我

概述

各种测试,各种查资料,总算解决了底部上滑锁屏功能的实现。一个底部三大金钢折腾死个人。
最后借助的还是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 {@Overridepublic void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {}@Overridepublic void onInterrupt() {}
}

为了实现悬浮窗的绘制,再重载两个方法,开始想放在 oncreate 方法里,出了点小问题,就放在onServiceConnected里了

    @Overrideprotected void onServiceConnected() {initWindow();super.onServiceConnected();}@Overridepublic 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 设置LayoutParamsWindowManager.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.LEFTlayoutParams.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;@Overridepublic 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
配置文件里面也只加个描述信息就行,多了竟给手机加负担


<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));
}