最近想了解一下android的悬浮窗是怎么实现的,然后就进行了尝试。
在activity中执行下面这段代码是可行的。
WindowManager windowManager = getSystemService(WindowManager.class);
Button button =new Button(this);
button.setText("111");
WindowManager.LayoutParams layoutParams =new WindowManager.LayoutParams();
layoutParams.width =100;
layoutParams.height =100;
layoutParams.gravity =Gravity.CENTER;
windowManager.addView(button,layoutParams);
在windowManager中直接添加view。正常执行,但是在Application的onCreate方法中执行,或者在Service的onCreate
方法中执行都会报错: Caused by: android.view.WindowManager$BadTokenException:
Unable to add window -- token null is not valid; is your activity running?
然后就想着怎么样去解决这个错误,一开始以为是没有token。 就去翻阅Toast的show方法实现流程。发现
Toast的 layoutParams.token=windowToken。然后就去查看windowToken是怎么创建的,得出结论是Binder token =new Binder();这样直接创建的并没有什么特殊的。然后我又进行了尝试:
WindowManager windowManager = getSystemService(WindowManager.class);
Button button =new Button(this);
button.setText("111");
WindowManager.LayoutParams layoutParams =new WindowManager.LayoutParams();
layoutParams.width =100;
layoutParams.height =100;
layoutParams.gravity =Gravity.CENTER;
layoutParams.token =new Binder();//这里添加了一个自己创建的token。
windowManager.addView(button,layoutParams);
上面的代码在application的onCreate方法和service的onCreate方法中执行,还是报了上面的错。这就遭难了......
以前知道layoutParams.type这个变量是控制视图层级的,但是也一直没有很深入的了解每个层级对应的值是哪些,因为报错是说token为Null
也没往这个type上想。后面进行了网上搜索之后,发现一些网友的说法这个type很重要。然后就尝试给type进行赋值。代码修改如下:
WindowManager windowManager = getSystemService(WindowManager.class);
Button button =new Button(this);
button.setText("111");
WindowManager.LayoutParams layoutParams =new WindowManager.LayoutParams();
layoutParams.width =100;
layoutParams.height =100;
layoutParams.gravity =Gravity.CENTER;
//layoutParams.type =WindowManager.LayoutParams.TYPE_PHONE; //这个值报错了 说没有权限,因为现实悬浮窗需要系统权限。
//layoutParams.type =WindowManager.LayoutParams.TYPE_TOAST; //这个值报错了 说没有权限,因为现实悬浮窗需要系统权限。
windowManager.addView(button,layoutParams);
总结:经过上面的两次修改还是不能成功运行,但是它没有报token为null的异常错误了,这是好事。那接下来就是对这个type的值的尝试了。
layoutParams.type =WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;这个值加上之后就能正常添加view了
最后经过尝试,发现type =WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;这个值是可以在除activity外正常运行的,其他的试了好几个也不行。所以以后如果能满足需求就直接使用这个type。如果不能满足再尝试其他的吧。这个值是添加的view会在应用的上表层显示。
在application的onCreate方法或者在service的onCreate方法里面运行下面代码是正常的。
WindowManager windowManager = getSystemService(WindowManager.class);
Button button =new Button(this);
button.setText("111");
WindowManager.LayoutParams layoutParams =new WindowManager.LayoutParams();
layoutParams.width =100;
layoutParams.height =100;
layoutParams.gravity =Gravity.CENTER;
layoutParams.type =WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; //这个type是可以正常运行的
windowManager.addView(button,layoutParams);
至此:我们就能通过windowManager去添加view了。
同时解决了两个异常:
异常1:Caused by: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@e856f1c -- permission denied for window type 2999
异常2: Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?