天天看点

android悬浮窗的实现

悬浮窗原理

思路:

1.建立一个服务,并且在里面生成一个WindowManager对象,通过它来加载一个视图作为悬浮窗。

2.设置WindowManager的参数LayoutParams

3.设置一个容器来找到悬浮窗的父控件,并绑定到windowManager中去

4.通过父控件来加载悬浮窗的视图

这个思路需要注意的两个细节:

a. WindowManager的参数LayoutParams中的type字段,目前主流的做法是将改字段赋值为LayoutParams.TYPE_PHONE.

b. 需要在AndroidManifest中加上相应的权限

否则无法出现悬浮窗。

上面省略的布局文件的说明,详情可以参考代码部分。

到这里,如果运气好的话,你很容易就可以用代码实现一个悬浮窗,再复杂一点的还可以顺便实现拖动效果,然而现实总是很残酷的,由于安卓系统的碎片化现象太严重,各家ROM对这个悬浮窗做了各种限制,比如小米的miui、魅族和锤子等只有用户进入到应用设置页面,打开悬浮窗开关才能显示悬浮窗(如图1和图2),这就比较麻烦了,从而导致早期悬浮窗在实际中比较难以推广。

android悬浮窗的实现

图 1 锤子系统

android悬浮窗的实现

图 2 MIUI系统

后来发现安卓本身有一些特殊的方式可以绕过这个权限检查,即将LayoutParams的type值设置成LayoutParams.TYPE_TOAST,如此一来开发者不需要再在AndroidManifest中添加权限,还可以使得App避开第三方ROM的权限检查,无需开启开关就能显示出悬浮窗,至此世界很美好。

但是,TYPE_TOAST也是有本身的局限性的,即从4.4以后的版本开始,使用TYPE_TOAST方式显示出来的悬浮窗能接收触摸事件和按键事件,而4.4以前只能显示出来,不能交互

可以通过附件中的demo体验一下悬浮窗效果,增加了触摸和点击事件。

效果图:

android悬浮窗的实现

图 3

本文中借鉴了其他文章的相关资料

下面是启动service时执行的显示悬浮窗的主要代码

/**
* 初始化windowManager
*/
private void initWindow(){
wmParams = new WindowManager.LayoutParams();
//获取的是WindowManagerImpl.CompatModeWrapper
mWindowManager = (WindowManager)getApplication().getSystemService(getApplication().WINDOW_SERVICE);
//设置window type 下面变量是在屏幕区域显示,则可以显示在状态栏之上
wmParams.type = WindowManager.LayoutParams.TYPE_TOAST;//无需权限
//wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;//需要权限,且在某些系统中还需要手动打开设置,比如miui
//设置图片格式,效果为背景透明
wmParams.format = PixelFormat.RGBA_8888;
//设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作)
wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
//调整悬浮窗显示的停靠位置为左侧置顶
wmParams.gravity = Gravity.LEFT | Gravity.TOP;
// 以屏幕左上角为原点,设置x、y初始值,相对于gravity
wmParams.x = ;wmParams.y = 0;
//设置悬浮窗口长宽数据
wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
LayoutInflater inflater = LayoutInflater.from(getApplication());
//获取浮动窗口视图所在布局
mFloatLayout = (LinearLayout) inflater.inflate(R.layout.float_layout, null);
//添加mFloatLayout
mWindowManager.addView(mFloatLayout, wmParams);
//浮动窗口按钮
mFloatView = (Button)mFloatLayout.findViewById(R.id.float_id);    mFloatLayout.measure(View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); 
}
           

顺便提一下在在小米系统中,如果使用TYPE_PHONE方式实现悬浮窗,则最好先判断是否打开了显示悬浮窗的开关,如果没有打开,则跳转到应用设置详情页面,跳转方法如下:

Uri packageURI = Uri.parse("package:" + "你的应用包名");  
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);  
startActivity(intent);  
           

源码下载:android实现悬浮窗功能