第二屆 Google 暑期大學生博客分享大賽 – 2011 Android 成長篇
本文為參加Google暑期大學生博客分享大賽特別撰寫,。
—————————————————————-
大家對懸浮窗概念不會陌生,,相信每臺電腦桌面的右上角都會有這么一個東西,,它總是出現(xiàn)在所有頁面的頂端(Top Show),。但在Android平臺中如何實現(xiàn)這樣的效果呢,?先來看一看效果圖。
看見在Google搜索框上面的那個Icon圖片了嘛,。下面我就來詳細介紹一下在Android平臺下懸浮窗口的實現(xiàn),,并讓它能夠隨手指的觸摸而移動。
一,、實現(xiàn)原理及移動思路
調(diào)用WindowManager,,并設置WindowManager.LayoutParams的相關屬性,通過WindowManager的addView方法創(chuàng)建View,,這樣產(chǎn)生出來的View根據(jù)WindowManager.LayoutParams屬性不同,,效果也就不同了。比如創(chuàng)建系統(tǒng)頂級窗口,,實現(xiàn)懸浮窗口效果,!然后通過覆寫懸浮View中onTouchEvent方法來改變windowMananager.LayoutParams中x和y的值來實現(xiàn)自由移動懸浮窗口。
二,、示例代碼
先來看一看懸浮View的代碼,,這里用一個ImageView作為演示
01 |
public class MyFloatView extends ImageView { |
02 |
private float mTouchStartX; |
03 |
private float mTouchStartY; |
07 |
private WindowManager wm=(WindowManager)getContext().getApplicationContext().getSystemService( "window" ); |
09 |
private WindowManager.LayoutParams wmParams = ((MyApplication)getContext().getApplicationContext()).getMywmParams(); |
11 |
public MyFloatView(Context context) { |
17 |
public boolean onTouchEvent(MotionEvent event) { |
20 |
y = event.getRawY()- 25 ; |
21 |
Log.i( "currP" , "currX" +x+ "====currY" +y); |
22 |
switch (event.getAction()) { |
23 |
case MotionEvent.ACTION_DOWN: |
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的方式來創(chuàng)建全局變量,示例代碼如下:
01 |
public class MyApplication extends Application { |
05 |
* 全局變量一般都比較傾向于創(chuàng)建一個單獨的數(shù)據(jù)類文件,并使用static靜態(tài)變量 |
07 |
* 這里使用了在Application中添加數(shù)據(jù)的方法實現(xiàn)全局變量 |
08 |
* 注意在AndroidManifest.xml中的Application節(jié)點添加android:name=".MyApplication"屬性 |
11 |
private WindowManager.LayoutParams wmParams= new WindowManager.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); |
25 |
wm=(WindowManager)getApplicationContext().getSystemService( "window" ); |
27 |
wmParams = ((MyApplication)getApplication()).getMywmParams(); |
30 |
*以下都是WindowManager.LayoutParams的相關屬性 |
33 |
wmParams.type=LayoutParams.TYPE_PHONE; |
34 |
wmParams.format=PixelFormat.RGBA_8888; |
37 |
wmParams.flags=LayoutParams.FLAG_NOT_TOUCH_MODAL |
38 |
| LayoutParams.FLAG_NOT_FOCUSABLE; |
48 |
wmParams.gravity=Gravity.LEFT|Gravity.TOP; |
58 |
wm.addView(myFV, wmParams); |
63 |
public void onDestroy(){ |
最后,,別忘了在AndroidManifest.xml中添加權限:
1 |
<USES-PERMISSION android:name= "android.permission.SYSTEM_ALERT_WINDOW" /> |
這樣一個可以置頂顯示、懸浮,、且可自由移動的窗口就完工了,。運行一下,然后按Home鍵返回桌面試試看(不能點擊返回鍵,,演示程序這里設置了銷毀窗體)
三,、一些說明
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 ; |
09 |
* Window type: system window, such as low power alert. These windows |
10 |
* are always on top of application windows. |
12 |
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平臺下的懸浮窗口,,有人說很不友好,有人很困惑哪里會用到,。事實上,,在一些軟件里面,懸浮窗口的設計給它們帶來了很大的優(yōu)勢,,比如流量監(jiān)控,,比如歌詞顯示。
給出源碼包下載