Android悬浮窗开启 适配所有机型(附源码)

1.开启悬浮窗权限

清单文件中添加:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

2.检测是否开启了悬浮窗权限

public static boolean canDrawOverlays(Context context) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return true;
        else if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
            return Settings.canDrawOverlays(context);
        } else {
            if (Settings.canDrawOverlays(context)) return true;
            try {
                WindowManager mgr = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
                if (mgr == null) return false; //getSystemService might return null
                View viewToAdd = new View(context);
                WindowManager.LayoutParams params = new WindowManager.LayoutParams(0, 0, android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O ?
                        WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                        WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
                viewToAdd.setLayoutParams(params);
                mgr.addView(viewToAdd, params);
                mgr.removeView(viewToAdd);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
    }

3.Activity调用

if (canDrawOverlays(getApplicationContext())) {
            myWindowManager = MyWindowManager.getInstance();
            myWindowManager.createNormalView(getApplicationContext(), this);
        } else {
           requestSettingCanDrawOverlays();
        }

4.开启悬浮框

private void requestSettingCanDrawOverlays() {
        int sdkInt = Build.VERSION.SDK_INT;
        if (sdkInt >= Build.VERSION_CODES.O) {//8.0以上
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
            startActivityForResult(intent, OPEN_OVERLAY_PERMISSION);
        } else if (sdkInt >= Build.VERSION_CODES.M) {//6.0-8.0
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
            intent.setData(Uri.parse("package:" + getPackageName()));
            startActivityForResult(intent, OPEN_OVERLAY_PERMISSION);
        } else {//4.4-6.0以下
            //无需处理了
        }
    }

5.Activity开启悬浮框回调

@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        //浮窗权限申请
        if (requestCode == OPEN_OVERLAY_PERMISSION) {
            new CountDownTimer(1000, 100) {
                @Override
                public void onTick(long millisUntilFinished) {
                }

                @Override
                public void onFinish() {
                    if (DeviceTools.canDrawOverlays(getApplicationContext())) {
                        myWindowManager = MyWindowManager.getInstance();
                        myWindowManager.createNormalView(getApplicationContext(), MainActivity.this);
                    } else {
                        Toast.makeText(MainActivity.this, "您拒绝了此权限,将无法显示悬浮窗", Toast.LENGTH_SHORT).show();
                    }
                }
            }.start();
        }

    }

6.MyWindowManager代码

/**
 * 悬浮窗管理器
 * 创建,移除
 * 单例模式
 */
public class MyWindowManager {

    private FloatNormalView normalView;

    private static MyWindowManager instance;
    private WindowManager windowManager;

    private MyWindowManager() {
    }

    public static MyWindowManager getInstance() {
        if (instance == null)
            instance = new MyWindowManager();
        return instance;
    }

    private WindowManager getWindowManager(Context context) {
        if (windowManager == null)
            windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        return windowManager;
    }

    public void setContent(String talkResult){
        if (normalView != null) {
            normalView.setResultContent(talkResult);
        }
    }

    /**
     * 创建小型悬浮窗
     */
    public Activity activity = null;
    public void createNormalView(Context context, Activity activity) {
        if (normalView == null) {
            normalView = new FloatNormalView(context,activity);
        }
        this.getWindowManager(context);
        this.activity = activity;
    }


    /**
     * 移除悬浮窗
     *
     * @param context
     */
    public void removeNormalView(Context context) {
        if (normalView != null) {
            windowManager.removeView(normalView);
            normalView = null;
        }
    }


}

7.FloatNormalView代码

/**
 * 悬浮窗
 */
public class FloatNormalView extends LinearLayout {

    /**
     * 记录小悬浮窗的宽度
     */
    public static int viewWidth;

    /**
     * 记录小悬浮窗的高度
     */
    public static int viewHeight;

    /**
     * 记录系统状态栏的高度
     */
    private static int statusBarHeight;

    /**
     * 用于更新小悬浮窗的位置
     */
    private WindowManager windowManager;

    /**
     * 小悬浮窗的参数
     */
    private WindowManager.LayoutParams mParams;

    /**
     * 记录当前手指位置在屏幕上的横坐标值
     */
    private float xInScreen;

    /**
     * 记录当前手指位置在屏幕上的纵坐标值
     */
    private float yInScreen;

    /**
     * 记录手指按下时在屏幕上的横坐标的值
     */
    private float xDownInScreen;

    /**
     * 记录手指按下时在屏幕上的纵坐标的值
     */
    private float yDownInScreen;

    /**
     * 记录手指按下时在小悬浮窗的View上的横坐标的值
     */
    private float xInView;

    /**
     * 记录手指按下时在小悬浮窗的View上的纵坐标的值
     */
    private float yInView;

    private Context mContext = null;

    private ActivityManager activityManager = null;

    private Activity mActivity = null;

    private TextView tvResult = null;


    public FloatNormalView(Context context, Activity activity) {
        super(context);
        mContext = context;
        mActivity = activity;
        windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); ;
        LayoutInflater.from(context).inflate(R.layout.float_normal_view, this);
        View view = findViewById(R.id.ll_float_normal);
        viewWidth = view.getLayoutParams().width;
        viewHeight = view.getLayoutParams().height;
        tvResult = view.findViewById(R.id.tv_result);
        initLayoutParams();
    }

    /**
     * 初始化参数
     */
    private void initLayoutParams() {
        //屏幕宽高
        int screenWidth = windowManager.getDefaultDisplay().getWidth();
        int screenHeight = windowManager.getDefaultDisplay().getHeight();

        mParams = new WindowManager.LayoutParams();
        //总是出现在应用程序窗口之上。
        mParams.type = WindowManager.LayoutParams.TYPE_PHONE;

        // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口
        // FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按,不设置这个flag的话,home页的划屏会有问题
        mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;

        //悬浮窗默认显示的位置
        mParams.gravity = Gravity.RIGHT | Gravity.TOP;
        //指定位置
        mParams.x = screenWidth - viewWidth * 2;
        mParams.y = screenHeight / 2 + viewHeight * 2;
        //悬浮窗的宽高
        mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        mParams.format = PixelFormat.TRANSPARENT;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;// 系统提示window
        }
        windowManager.addView(this, mParams);
    }

    public void setResultContent(String talkResult){
        tvResult.setText(talkResult);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 手指按下时记录必要数据,纵坐标的值都需要减去状态栏高度
                xInView = event.getX();
                yInView = event.getY();
                xDownInScreen = event.getRawX();
                yDownInScreen = event.getRawY() - getStatusBarHeight();
                xInScreen = event.getRawX();
                yInScreen = event.getRawY() - getStatusBarHeight();
                break;
            case MotionEvent.ACTION_MOVE:
                xInScreen = event.getRawX();
                yInScreen = event.getRawY() - getStatusBarHeight();
                // 手指移动的时候更新小悬浮窗的位置
                updateViewPosition();
                break;
            case MotionEvent.ACTION_UP:
                // 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。
                if (xDownInScreen == xInScreen && yDownInScreen == yInScreen) {
                    if (!DeviceTools.isRunForeground()){
                        Log.e("app---","后台");
//                        activityManager.moveTaskToFront(mActivity.getTaskId(), ActivityManager.MOVE_TASK_WITH_HOME);
                        EventBus.getDefault().post(new DVRConnectBean(Type.DeviceCodeStatus.UNKNOWN, EventBusTag.INSTALL_APP));
                    }
                }
                break;
            default:
                break;
        }
        return true;
    }

    public boolean IsForeground(Context context) {
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
        if (tasks != null && !tasks.isEmpty()) {
            ComponentName topActivity = tasks.get(0).topActivity;
            if (topActivity.getPackageName().equals(context.getPackageName())) {
                return true;
            }
        }
        return false;
    }

    /**
     * 将小悬浮窗的参数传入,用于更新小悬浮窗的位置。
     *
     * @param params 小悬浮窗的参数
     */
    public void setParams(WindowManager.LayoutParams params) {
        mParams = params;
    }

    /**
     * 更新小悬浮窗在屏幕中的位置。
     */
    private void updateViewPosition() {
//        mParams.x = (int) (xInScreen - xInView);
        mParams.y = (int) (yInScreen - yInView);
        windowManager.updateViewLayout(this, mParams);
    }

    /**
     * 用于获取状态栏的高度。
     *
     * @return 返回状态栏高度的像素值。
     */
    private int getStatusBarHeight() {
        if (statusBarHeight == 0) {
            try {
                Class<?> c = Class.forName("com.android.internal.R$dimen");
                Object o = c.newInstance();
                Field field = c.getField("status_bar_height");
                int x = (Integer) field.get(o);
                statusBarHeight = getResources().getDimensionPixelSize(x);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return statusBarHeight;
    }



}

意见反馈

如有问题请及时联系我,及时修复问题