第二届 Google 暑期大学生博客分享大赛 – 2011 Android 成长篇 java
本文为参加Google暑期大学生博客分享大赛特别撰写。 android
—————————————————————- 面试
你们对悬浮窗概念不会陌生,相信每台电脑桌面的右上角都会有这么一个东西,它老是出如今全部页面的顶端(Top Show)。但在Android平台中如何实现这样的效果呢?先来看一看效果图。 windows
看见在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; |
07 |
private WindowManager wm=(WindowManager)getContext().getApplicationContext().getSystemService("window"); |
08 |
//此wmParams变量为获取的全局变量,用以保存悬浮窗口的属性 |
09 |
private WindowManager.LayoutParams wmParams = ((MyApplication)getContext().getApplicationContext()).getMywmParams(); |
11 |
public MyFloatView(Context context) { |
13 |
// TODO Auto-generated constructor stub |
17 |
public boolean onTouchEvent(MotionEvent event) { |
18 |
//获取相对屏幕的坐标,即以屏幕左上角为原点 |
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); |
30 |
case MotionEvent.ACTION_MOVE: //捕获手指触摸移动动做 |
34 |
case MotionEvent.ACTION_UP: //捕获手指触摸离开动做 |
36 |
mTouchStartX=mTouchStartY=0; |
42 |
private void updateViewPosition(){ |
44 |
wmParams.x=(int)( x-mTouchStartX); |
45 |
wmParams.y=(int) (y-mTouchStartY); |
46 |
wm.updateViewLayout(this, wmParams); //刷新显示 |
上面的wmParams变量(即WindowManager.LayoutParams)的存储采用了extends Application的方式来建立全局变量,示例代码以下: spa
01 |
public class MyApplication extends Application { |
05 |
* 全局变量通常都比较倾向于建立一个单独的数据类文件,并使用static静态变量 |
07 |
* 这里使用了在Application中添加数据的方法实现全局变量 |
08 |
* 注意在AndroidManifest.xml中的Application节点添加android:name=".MyApplication"属性 |
11 |
private WindowManager.LayoutParams wmParams=newWindowManager.LayoutParams(); |
13 |
public WindowManager.LayoutParams getMywmParams(){ |
再来看一看Activity中的代码:
01 |
public class MyFloatViewActivity extends Activity { |
02 |
/** Called when the activity is first created. */ |
04 |
private WindowManager wm=null; |
05 |
private WindowManager.LayoutParams wmParams=null; |
07 |
private MyFloatView myFV=null; |
11 |
public void onCreate(Bundle savedInstanceState) { |
12 |
super.onCreate(savedInstanceState); |
13 |
setContentView(R.layout.main); |
21 |
private void createView(){ |
22 |
myFV=new MyFloatView(getApplicationContext()); |
23 |
myFV.setImageResource(R.drawable.icon); //这里简单的用自带的Icom来作演示 |
25 |
wm=(WindowManager)getApplicationContext().getSystemService("window"); |
26 |
//设置LayoutParams(全局变量)相关参数 |
27 |
wmParams = ((MyApplication)getApplication()).getMywmParams(); |
30 |
*如下都是WindowManager.LayoutParams的相关属性 |
33 |
wmParams.type=LayoutParams.TYPE_PHONE; //设置window type |
34 |
wmParams.format=PixelFormat.RGBA_8888; //设置图片格式,效果为背景透明 |
37 |
wmParams.flags=LayoutParams.FLAG_NOT_TOUCH_MODAL |
38 |
| LayoutParams.FLAG_NOT_FOCUSABLE; |
40 |
* 下面的flags属性的效果形同“锁定”。 |
41 |
* 悬浮窗不可触摸,不接受任何事件,同时不影响后面的事件响应。 |
42 |
wmParams.flags=LayoutParams.FLAG_NOT_TOUCH_MODAL |
43 |
| LayoutParams.FLAG_NOT_FOCUSABLE |
44 |
| LayoutParams.FLAG_NOT_TOUCHABLE; |
48 |
wmParams.gravity=Gravity.LEFT|Gravity.TOP; //调整悬浮窗口至左上角,便于调整坐标 |
58 |
wm.addView(myFV, wmParams); |
63 |
public void onDestroy(){ |
65 |
//在程序退出(Activity销毁)时销毁悬浮窗口 |
最后,别忘了在AndroidManifest.xml中添加权限:
1 |
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"> |
这样一个能够置顶显示、悬浮、且可自由移动的窗口就完工了。运行一下,而后按Home键返回桌面试试看(不能点击返回键,演示程序这里设置了销毁窗体)
3、一些说明
WindowManager的方法很简单,基本用到的就三个addView,removeView,updateViewLayout。
而WindowManager.LayoutParams的属性就多了,很是丰富,这个也是关键所在。
这里例举两个window type:
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 |
07 |
public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2; |
10 |
* Window type: system window, such as low power alert. These windows |
11 |
* are always on top of application windows. |
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; |
04 |
/** Window flag: this window can never receive touch events. */ |
05 |
public static final int FLAG_NOT_TOUCHABLE = 0x00000010; |
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平台下的悬浮窗口,有人说很不友好,有人很困惑哪里会用到。事实上,在一些软件里面,悬浮窗口的设计给它们带来了很大的优点,好比流量监控,好比歌词显示。