天天看點

android底部上滑鎖屏概述TYPE_ACCESSIBILITY_OVERLAYAccessibilityService

概述

各種測試,各種查資料,總算解決了底部上滑鎖屏功能的實作。一個底部三大金鋼折騰死個人。

最後借助的還是AccessibilityService實作。因為我想實作的底部導航欄一定要顯示在螢幕最底端,就算有三大金剛navigationbar,也要置于其頂層。

為了效果顯示,我沒有設定透明度,正常應用時,我是把背景色取消了

android底部上滑鎖屏概述TYPE_ACCESSIBILITY_OVERLAYAccessibilityService

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