android 悬浮窗口

来源:互联网 发布:mac会变的吗 编辑:程序博客网 时间:2024/05/01 02:22
 

本文为参加Google暑期大学生博客分享大赛特别撰写。


—————————————————————-

大家对悬浮窗概念不会陌生,相信每台电脑桌面的右上角都会有这么一个东西,它总是出现在所有页面的顶端(Top Show)。但在Android平台中如何实现这样的效果呢?先来看一看效果图。

FloatView

看见在Google搜索框上面的那个Icon图片了嘛。下面我就来详细介绍一下在Android平台下悬浮窗口的实现,并让它能够随手指的触摸而移动。

一、实现原理及移动思路

调用WindowManager,并设置WindowManager.LayoutParams的相关属性,通过WindowManager的addView方法创建View,这样产生出来的View根据WindowManager.LayoutParams属性不同,效果也就不同了。比如创建系统顶级窗口,实现悬浮窗口效果!然后通过覆写悬浮View中onTouchEvent方法来改变windowMananager.LayoutParams中x和y的值来实现自由移动悬浮窗口。

二、示例代码

先来看一看悬浮View的代码,这里用一个ImageView作为演示

view source
print?
01public class MyFloatView extendsImageView {
02    privatefloat mTouchStartX;
03    privatefloat mTouchStartY;
04    privatefloat x;
05    privatefloat y;
06  
07    privateWindowManager wm=(WindowManager)getContext().getApplicationContext().getSystemService("window");
08    //此wmParams变量为获取的全局变量,用以保存悬浮窗口的属性
09    privateWindowManager.LayoutParams wmParams = ((MyApplication)getContext().getApplicationContext()).getMywmParams();
10  
11    publicMyFloatView(Context context) {
12        super(context);
13        // TODO Auto-generated constructor stub
14    }
15  
16     @Override
17     publicboolean onTouchEvent(MotionEvent event) {
18         //获取相对屏幕的坐标,即以屏幕左上角为原点
19         x = event.getRawX();
20         y = event.getRawY()-25;  //25是系统状态栏的高度
21         Log.i("currP","currX"+x+"====currY"+y);
22         switch(event.getAction()) {
23            caseMotionEvent.ACTION_DOWN:    //捕获手指触摸按下动作
24                //获取相对View的坐标,即以此View左上角为原点
25                mTouchStartX =  event.getX();
26                    mTouchStartY =  event.getY();
27                Log.i("startP","startX"+mTouchStartX+"====startY"+mTouchStartY);
28                break;
29  
30            caseMotionEvent.ACTION_MOVE:   //捕获手指触摸移动动作
31                updateViewPosition();
32                break;
33  
34            caseMotionEvent.ACTION_UP:    //捕获手指触摸离开动作
35                updateViewPosition();
36                mTouchStartX=mTouchStartY=0;
37                break;
38            }
39            returntrue;
40        }
41  
42     privatevoid updateViewPosition(){
43        //更新浮动窗口位置参数
44        wmParams.x=(int)( x-mTouchStartX);
45        wmParams.y=(int) (y-mTouchStartY);
46        wm.updateViewLayout(this, wmParams); //刷新显示
47     }
48  
49}

上面的wmParams变量(即WindowManager.LayoutParams)的存储采用了extends Application的方式来创建全局变量,示例代码如下:

view source
print?
01public class MyApplication extendsApplication {
02  
03    /**
04     * 创建全局变量
05     * 全局变量一般都比较倾向于创建一个单独的数据类文件,并使用static静态变量
06     *
07     * 这里使用了在Application中添加数据的方法实现全局变量
08     * 注意在AndroidManifest.xml中的Application节点添加android:name=".MyApplication"属性
09     *
10     */
11    privateWindowManager.LayoutParams wmParams=newWindowManager.LayoutParams();
12  
13    publicWindowManager.LayoutParams getMywmParams(){
14        returnwmParams;
15    }
16}

再来看一看Activity中的代码:

view source
print?
01public class MyFloatViewActivity extendsActivity {
02    /** Called when the activity is first created. */
03  
04    privateWindowManager wm=null;
05    privateWindowManager.LayoutParams wmParams=null;
06  
07    privateMyFloatView myFV=null;
08  
09    @Override
10    publicvoid onCreate(Bundle savedInstanceState) {
11        super.onCreate(savedInstanceState);
12        setContentView(R.layout.main);
13        //创建悬浮窗口
14        createView();
15  
16    }
17  
18    privatevoid createView(){
19        myFV=newMyFloatView(getApplicationContext());
20        myFV.setImageResource(R.drawable.icon); //这里简单的用自带的Icom来做演示
21        //获取WindowManager
22        wm=(WindowManager)getApplicationContext().getSystemService("window");
23        //设置LayoutParams(全局变量)相关参数
24        wmParams = ((MyApplication)getApplication()).getMywmParams();
25  
26         /**
27         *以下都是WindowManager.LayoutParams的相关属性
28         * 具体用途可参考SDK文档
29         */
30        wmParams.type=LayoutParams.TYPE_PHONE;  //设置window type
31        wmParams.format=PixelFormat.RGBA_8888;  //设置图片格式,效果为背景透明
32  
33        //设置Window flag
34        wmParams.flags=LayoutParams.FLAG_NOT_TOUCH_MODAL
35                              | LayoutParams.FLAG_NOT_FOCUSABLE;
36        /*
37         * 下面的flags属性的效果形同“锁定”。
38         * 悬浮窗不可触摸,不接受任何事件,同时不影响后面的事件响应。
39         wmParams.flags=LayoutParams.FLAG_NOT_TOUCH_MODAL
40                               | LayoutParams.FLAG_NOT_FOCUSABLE
41                               | LayoutParams.FLAG_NOT_TOUCHABLE;
42        */
43  
44        wmParams.gravity=Gravity.LEFT|Gravity.TOP;  //调整悬浮窗口至左上角,便于调整坐标
45        //以屏幕左上角为原点,设置x、y初始值
46        wmParams.x=0;
47        wmParams.y=0;
48  
49        //设置悬浮窗口长宽数据
50        wmParams.width=40;
51        wmParams.height=40;
52  
53        //显示myFloatView图像
54        wm.addView(myFV, wmParams);
55  
56    }
57  
58    @Override
59    publicvoid onDestroy(){
60        super.onDestroy();
61        //在程序退出(Activity销毁)时销毁悬浮窗口
62        wm.removeView(myFV);
63    }
64}

最后,别忘了在AndroidManifest.xml中添加权限:

view source
print?
1<USES-PERMISSION style="BEHAVIOR: none"isImg="false" android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

这样一个可以置顶显示、悬浮、且可自由移动的窗口就完工了。运行一下,然后按Home键返回桌面试试看(不能点击返回键,演示程序这里设置了销毁窗体)

三、一些说明

WindowManager的方法很简单,基本用到的就三个addView,removeView,updateViewLayout。

而WindowManager.LayoutParams的属性就多了,非常丰富,这个也是关键所在。

这里例举两个window type:

view source
print?
01/**
02 * Window type: phone.  These are non-application windows providing
03 * user interaction with the phone (in particular incoming calls).
04 * These windows are normally placed above all applications, but behind
05 * the status bar.
06 */
07public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;
08/**
09 * Window type: system window, such as low power alert. These windows
10 * are always on top of application windows.
11 */
12public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;

可以看出TYPE_SYSTEM_ALERT的显示层次比TYPE_PHONE还要高,有兴趣的可以试一试显示效果哦!

另外关键的window flag:

view source
print?
01/** Window flag: this window won't ever get focus. */
02public static final int FLAG_NOT_FOCUSABLE      = 0x00000008;
03/** Window flag: this window can never receive touch events. */
04public static final int FLAG_NOT_TOUCHABLE      = 0x00000010;
05/** Window flag: Even when this window is focusable (its
06 * {@link #FLAG_NOT_FOCUSABLE is not set), allow any pointer events
07 * outside of the window to be sent to the windows behind it.  Otherwise
08 * it will consume all pointer events itself, regardless of whether they
09 * are inside of the window. */
10public static final int FLAG_NOT_TOUCH_MODAL    = 0x00000020;

详细的可以看一下这里。

最后,关于Android平台下的悬浮窗口,有人说很不友好,有人很困惑哪里会用到。事实上,在一些软件里面,悬浮窗口的设计给它们带来了很大的优势,比如流量监控,比如歌词显示。

原创粉丝点击