Android中可自由移动悬浮窗口的实现

第二届 Google 暑期大学生博客分享大赛 – 2011 Android 成长篇 java

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


—————————————————————- 面试

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

FloatView

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

1、实现原理及移动思路 less

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

2、示例代码 this

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

01 public class MyFloatView extends ImageView {
02     private float mTouchStartX;
03     private float mTouchStartY;
04     private float x;
05     private float y;
06      
07     private WindowManager wm=(WindowManager)getContext().getApplicationContext().getSystemService("window");
08     //此wmParams变量为获取的全局变量,用以保存悬浮窗口的属性
09     private WindowManager.LayoutParams wmParams = ((MyApplication)getContext().getApplicationContext()).getMywmParams();
10  
11     public MyFloatView(Context context) {
12         super(context);    
13         // TODO Auto-generated constructor stub
14     }
15      
16      @Override
17      public boolean 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             case MotionEvent.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             case MotionEvent.ACTION_MOVE:   //捕获手指触摸移动动做           
31                 updateViewPosition();
32                 break;
33  
34             case MotionEvent.ACTION_UP:    //捕获手指触摸离开动做
35                 updateViewPosition();
36                 mTouchStartX=mTouchStartY=0;
37                 break;
38             }
39             return true;
40         }
41       
42      private void 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的方式来建立全局变量,示例代码以下: spa

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

再来看一看Activity中的代码:

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

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

1 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW">
2 </uses-permission>

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

3、一些说明

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

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

这里例举两个window type:

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  */
07 public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;
08  
09 /**
10  * Window type: system window, such as low power alert. These windows
11  * are always on top of application windows.
12  */
13 public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;

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

另外关键的window flag:

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

详细的能够看一下这里

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

相关文章
相关标签/搜索