这一篇讲讲PopupWindow与悬浮窗之间那些不得不说的故事。
之所以把PopupWindow与悬浮窗这两个放到一起讲,是因为这两个的实现原理基本是一致的,只是有点不同而已。
原理:
使用系统服务(WindowManagerService)将要显示的View添加进Window中。
WindowManagerService和ActivityManagerService是Android系统中两个最重要的服务,其中一个管理窗口显示,一个管理四大组件。
ActivityManagerService这里呢先不讲,以后再说。
简单讲下WindowManagerService,想深入了解的建议看下老罗大大的博客,新手慎入:传送门
以一个最简单的HelloWorld来说明:
当Activity被启动时,Activity会被分配一个Window,一个window代表一个充满屏幕的窗口。然后当我们调用setContentView时,我们要显示的View(后面用contentView表示)就会被添加进该Activity的window中。window接收到contentView后会根据设置在其外部包裹一些其它view(比如titlebar),然后请求WindowManagerService,WindowManagerService则向底层一层一层发送请求直到硬件将其显示出来。
上面的说明简化了很多过程,因为不是我想讲的重点,这一篇只是想简单讲下怎么用WindowManagerService,而不是它的实现原理(原理我也讲不来。。)。
一般我们添加View的方式是将子view添加到一个已经存在于window上的父view中。但其实利用WindowManagerService我们是可以直接将view添加的window中的,就像系统帮我们自动将setContentView的contentView添加到window一样。
两种方式的主要区别在于子view添加进父view,那么子view和父view就位于同一个window,但用WindowManagerService添加view,我们可以指定不同window(如果你有足够权限的话),我这里说的不同window是指他们的等级不同。PopupWindow依附于Activity,属于应用级。而悬浮窗就是属于系统级,所以它才可以保持在前端显示(其它系统级的window还有来电提醒,ANR、FC等系统警告等)。
再说说WindowManagerService和WindowManager的区别:
WindowManagerService是系统服务,当并不完全对开发者开放,而是使用WindowManager做为代理,提供部分功能和包装后的功能,可以说WindowManager是阉割版的WindowManagerService,那些会危害系统安全的功能被屏蔽了。Android系统中的系统服务基本都是这种方式。
说完原理来看看代码证实一下,先看下系统实现的PopupWindow,在根据原理来实现一个悬浮窗。
PopupWindow:
其实PopupWindow里面只有两句核心的代码,其它的都是一些位置和大小的计算。
(1)在构造方法中,通过传入的context或则view获得WindowManager:
mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
或者:
mContext = contentView.getContext(); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
(2)通过WindowManager将PopupWindow添加进window:
mWindowManager.addView(mPopupView, p);
是不是炒鸡简单?
那么我们来开始实现悬浮窗:
(1)先获取WindowManager:
mWindowManager = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE);
注意这里要使用Application的context来获取WindowManager,如果使用activity.getSystemService(Context.WINDOW_SERVICE)来获取的话,得到的WindowManager的生命周期是小于等于Activity的,而使用上面这种方式得到的WindowManager是跟整个应用一样的,这样除非你的整个应用进程都被终结了,不然你的悬浮窗就可以一直显示。
(2)设置参数WindowManager.LayoutParams:
大小位置等可以根据需求来设置,但是要记住设置显示的级别:
wmParams.type = LayoutParams.TYPE_PHONE;
(3)将要显示的view添加进window:
mWindowManager.addView(floatView, wmParams);
当然你也可以对floatView和其子view进行点击、触摸等事件的监听,然后可以通过WindowManager的另一个方法来刷新floatView:
mWindowManager.updateViewLayout(floatView, wmParams);
举个栗子:
你可以监听触摸事件,得到触摸位置并设置进wmParams里,再调用updateViewLayout方法来实现悬浮窗随手指移动的效果。
一点补充:网上的悬浮窗demo很多都是在Service中生成的,其实在Activity生成是一样的。但Service的有个好处是可以让应用进程不那么容易被杀死。
楼主写的一个demo效果图:
这里的吃豆人是我再另一篇博文写得demo摘出来的,没啥好说的,有兴趣的可以去支持下:
终于摆脱每一篇都是讲AbsListView的厄运了。
求赞求评论