第二屆 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平臺下的懸浮窗口,有人說很不友好,有人很困惑哪裏會用到。事實上,在一些軟件裏面,懸浮窗口的設計給它們帶來了很大的優點,好比流量監控,好比歌詞顯示。