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

09-10 09:51:40.517 2518 2782 D WindowManager: powerPress: eventTime=100975 interactive=true count=1 mShortPressOnPowerBehavior=1 09-10 09:51:40.518 2518 2782 D PowerFingerprintServiceStubImpl: intervalFpPower gap:100975 09-10 09:51:40.518 2518 2782 D PowerFingerprintServiceStubImpl: intervalFpPower gap:100975 09-10 09:51:40.519 2518 2782 I PowerManagerService: goToSleepInternal[groupId =0], uid: 1000 pid: 2518 reason: power_button 09-10 09:51:40.519 2518 2782 I PowerGroup: Powering off display group due to power_button (groupId= 0, uid= 1000, millisSinceLastUserActivity=13713, lastUserActivityEvent=other)... 09-10 09:51:40.522 2518 2518 I WindowManager: Started going to sleep... (groupId=0 why=OFF_BECAUSE_OF_USER) 09-10 09:51:40.523 2518 2782 I PowerManagerService: sFlinger = android.os.BinderProxy@2c7d819 09-10 09:51:40.524 2518 2782 D DisplayPowerController[0]: screenActiveEarly = false 09-10 09:51:40.524 2518 9999 W FloatingWindowManager: Request remove window on display 0 09-10 09:51:40.525 2518 2782 D DisplayPowerState: setEarlyScreenUpdate: screenActiveEarly=false 09-10 09:51:40.525 2518 2782 I LocalDisplayAdapter: isAdvanceSetPowerState:flag = false 09-10 09:51:40.525 2518 2782 D DisplayPowerController[0]: isWakeUp = false, mIsNotAodActive = false, mScreenActiveEarly = false, mPowerRequest.policy = 3 09-10 09:51:40.525 2518 2782 I LocalDisplayAdapter: isInputFrozen = false 09-10 09:51:40.525 2518 2782 I PowerManagerService: Going to sleep due to power_button (uid 1000, screenOffTimeout=60000, activityTimeoutWM=-1, maxDimRatio=0.29999995, maxDimDur=18000, details=null)... 09-10 09:51:40.528 2518 3283 D BackgroundInstallControlService: Package event received: 0 09-10 09:51:40.528 2518 2841 I DisplayPowerControllerImpl: wait screen off animator: false -> true 09-10 09:51:40.529 2518 2841 I SceneDetector: mNeedCheckAonFlare:false, mNeedCheckProximitySensor:false 09-10 09:51:40.529 2518 2841 I DisplayPowerController[0]: BrightnessEvent: brt=0.114802286 (72.0%), nits= 103.728424, lux=125.76478, reason=automatic, strat=InvalidBrightnessStrategy, state=ON, stateReason=DEFAULT_POLICY, policy=OFF, flags=, initBrt=0.114802286, rcmdBrt=0.114802286, preBrt=1.0, preLux=10755.468, wasShortTermModelActive=false, autoBrightness=true (default), unclampedBrt=0.114802286, hbmMax=0.49975574, hbmMode=off, thrmMax=1.0, rbcStrength=50, powerFactor=1.0, physDisp=鍐呯疆灞忓箷(local:4630946446063160449), logicalId=0, slowChange=true, rampSpeed=0.0 09-10 09:51:40.530 2518 2753 D UidStateManager: uid state changed, uid=10168 09-10 09:51:40.533 2518 2753 D UidStateManager: uid state changed, uid=10127 09-10 09:51:40.533 2518 2782 I MiuiKeyIntercept: MIUI Keyboard Shortcut status is:false 09-10 09:51:40.534 2518 2518 D CompatChangeReporter: Compat change id reported: 313677553; UID 10117; state: ENABLED 09-10 09:51:40.539 3731 3912 D MiuiDragAndDropController: mStartedGoingToSleepListener: sending cancel event 09-10 09:51:40.539 3731 3912 D MulWinSwitchEventHandler: sendCancelEvent 09-10 09:51:40.542 2518 9999 D BarFollowAnimation: pairAppearanceRegionAndWin win=Window{c4fa5da u0 com.android.settings/com.android.settings.SubSettings} 8 09-10 09:51:40.543 3731 3912 D OneHandedController: stopOneHanded state is 0 09-10 09:51:40.544 2518 9999 D MiuiRefreshRatePolicy: reset 09-10 09:51:40.545 2518 3241 D QcomSchedBoost: stopBoostInternal 09-10 09:51:40.546 2518 3241 D QcomSchedBoost: startBoostInternal 1000 09-10 09:51:40.548 2518 9999 I SmartPower: system/1000(2518): visible->invisible(3079ms) R(become invisible) adj=-900. 09-10 09:51:40.549 2518 2828 E BatteryStatsService: note track volume change AudioToPower BatteryStatsservices 1000 3731 137 0.5011872 0.5011872 09-10 09:51:40.555 2518 6972 D PowerManagerService: [xiaomi_power]acquireWakeLockInternal: lock=45314385, flags=0x1, tag="AudioMix", ws=null, uid=1041, pid=0 09-10 09:51:40.555 2518 2828 E BatteryStatsService: note StartAudioToPower BatteryStatsservices1000 3731 137 38 2 09-10 09:51:40.556 2518 2828 E BatteryStatsService: note final volume change AudioToPower BatteryStatsservices 1000 3731 137 0.030198043 09-10 09:51:40.568 2349 2349 D [debug][sipa_driver] sipa_power_get: ucontrol = 0, channel_num = 0 09-10 09:51:40.568 2349 2349 D [debug][sipa_driver] sipa_power_set: ucontrol = 1, rst = 611, channel_num = 0 09-10 09:51:40.568 2349 2349 D [debug][sipa_driver] sipa_resume: si_pa->chip_type = 9, channel:0, running 09-10 09:51:40.569 970 970 E : [NVT-ts] nvt_panel_notifier_callback 3071: Notification type:4, early_trigger:1 09-10 09:51:40.569 970 970 E : [NVT-ts] nvt_panel_notifier_callback 3116: Received fps change old fps:60 new fps:120 09-10 09:51:40.569 970 970 E BATTERY_CHG: panel_event_notifier_callback: panel event received, type: 4 09-10 09:51:40.569 970 970 E BATTERY_CHG: panel_event_notifier_callback: send screen status<4> to adsp 09-10 09:51:40.570 970 970 I [drm:dsi_display_set_mode [msm_drm]] [msm-dsi-info]: mdp_transfer_time=0, hactive=720, vactive=1640, fps=120, clk_rate=0
最新发布
09-12
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值