note_33:悬浮窗

悬浮窗


参考:


文章目录


1. 权限

悬浮窗需要用户授权才能开启,如果在没有权限的情况下开启悬浮窗的话,会报window 2003的错误。
悬浮窗的权限会因为SDK版本而有所不同。对于SDK < 23的系统可以直接由app申请权限,而SDK >= 23的系统则必须通过用户授权才可以。
悬浮窗的权限跟普通的申请照相机图片通讯录等等资源不一样,申请这些资源的时候可以直接通过ActivityCompat.checkSelfPermission()来实现,系统会弹出一个对话框来提示用户允许还是不允许app获取这些权限。而悬浮窗不是,因为允许该应用在其他应用上方显示是在高级里面的,所以必须往Settings发送intent跳到开启悬浮窗的页面让用户手动打开允许。

private void requestPermission() {
	// 判断当前系统的SDK版本是否大于等于23
	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
		if (!Settings.canDrawOverlays(AppEn.appContext)) {
			Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
			intent.setData(Uri.parse("package:" + PACKGE_NAME)); // 这个应用程序的包名
			startActivityForResult(intent, REQUEST_PERMISSION_CODE); // 自己定义这个返回的确认码
		}
	}
}

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

	// 一个非常大的坑就是 只要app顺利跳到了Settings里这个app的高级页面之后
	// 不管用户有没有确认允许该应用显示在其他应用上方
	// 只要应用点击返回 都会返回这个REQUEST_PERMISSION_CODE
	// 所以应该在onActivityResult里面再一次判断究竟有没有成功获取权限
	if (requestCode == REQUEST_PERMISSION_CODE) {
		if (Settings.canDrawOverlays(PACKGE_NAMENAME)) {
			Log.d(TAG, "成功获取开启悬浮窗的权限!");
		}
	}
}

而在Manifest.xml里面也需要加上相关的uses-permission

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

2. 创建悬浮窗

(1) 布局及界面初始化

布局就是一层layout垫底,上面加别的东西就行了,可以是ImageView或者别的什么东西。总之就跟平时activity或者dialog或者fragment之类的布局一样就行了。

那些必要的findViewById()LayoutInflater.from(CONTEXT).inflate(R.layout.LAYOUT_NAME, null),以及各种必要的回调函数就还是按以往的一样。

View windowView;

private void initView() {
	windowView = LayoutInflater.from(CONTEXT).inflate(R.layout.LAYOUT_NAME, null);
}

(2) 参数初始化

因为悬浮窗是用WindowManager实现的,只要它依附的Service开启了并且没有stopSelf(),它就可以不受任何activity影响穿透整个app存在。
在参数初始化时,还是要在参数里面写明权限申请,就是把Manifest里面的两个uses-permission写一遍。
除此之外,还要初始化一下悬浮窗的长、宽、位置、是否透明、可不可以获取焦点之类的基础设置。

WindowManager windowManager = null;

private void initParams() {
	WindowManager.LayoutParams params = new WindowManager.LayoutParams();

	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
		params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
	} else {
		params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
	}

	windowManager = (WindowManager) CONTEXT.getSystemService(Context.WINDOW_SERVICE);

	params.format = PixelFormat.RGBA_8888; // 窗口透明
	params.gravity = Gravity.BOTTOM;
	params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
	params.width = WindowManager.LayoutParams.WRAP_CONTENT;
	params.height = WindowManager.LayoutParams.WRAP_CONTENT;
	params.x = CONTEXT.getResources.getDimensionPixelSize(R.dimen.DIMEN_X);
	params.y = CONTEXT.getResources.getDimensionPixelSize(R.dimen.DIMEN_Y);

	windowManager.addView(windowView, params); // windowView就是界面初始化的时候用来inflate的那个view
}

(4) 销毁悬浮窗

销毁悬浮窗的时候不能单纯地把windowManager置为null,也不要轻易使用removeViewImmediate,非常容易错。

private void destroyWindow() {
	windowManger.removeView(windowView);
}

3. Service

Service是安卓的四大组件之一,它是跑在前台的服务。它不像activity一样需要界面,不过它可以绑定一个view来处理事务。

Service分为两种,绑定的和不绑定的。这里暂时先用普通的,不绑定的Service。

对于这种不绑定的Service,生命周期是onCreate()onStartCommand()onDestroy()
当一个Service开启了之后,如果没有停止它的话,它将一直运行,除非被系统杀死。
一个Service首次创建的时候会调用onCreate(),但是一旦创建并且没有被停止,之后的每一次startService(),系统都不会调用onCreate(),而是从onStartCommand()开始跑。

@Override
public void onCreate() {
	super.onCreate();
	init();
}

private void init() {
	// 进行必要的初始化工作
	// 例如创建一个悬浮窗或者别的东西
	// ...
	// ...
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
	// 最好判断一下是否为空
	// 因为onStartCommand传进来的intent是Nullable的
	// 有时候系统不知道干什么就可能会莫名其妙传个null的intent进来
	if (intent == null) return super.onStartCommand(null, flags, startId);

	// 然后开始对悬浮窗进行想要的操作
	// ...
	// ...

	return super.onStartCommand(intent, flags, startId);
}

private void destroyService() {
	// 在结束service之前记得销毁悬浮窗
	// destroyWindow();
	stopSelf();
}

note_33:悬浮窗note_33:悬浮窗 Champagne&Caviar 发布了33 篇原创文章 · 获赞 4 · 访问量 1万+ 私信 关注
上一篇:android.view.WindowManager$BadTokenException 崩掉


下一篇:toolbar融入状态栏实现沉浸式遇到的问题