很多應用都有懸浮窗等功能,在手機螢幕上放置一個懸浮窗,使用者可以操作該懸浮窗,但是這種懸浮窗並不是widget,如下簡單介紹下懸浮窗:
一般螢幕放置view都是基於WindowManager類,然後再配合WindowManager.LayoutParams類設定不同的參數,可以達到不同的效果,如是否可以接受使用者輸入,是否有焦點等。
WindowManager一般通過Context.getSystemService(Context.WINDOW_SERVICE)的方式擷取,關於類的繼承關係為
public interface WindowManager extends ViewManager,ViewManager只有三個方法,見下:
/** Interface to let you add and remove child views to an Activity. To get an instance * of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}. */public interface ViewManager{ public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view);}
LayoutParams類的主要可以設定type和flag,具體參數可以參考源碼。
以下主要分析一段具體使用代碼:
WindowManager wm = null; WindowManager.LayoutParams wmParams = null; // 擷取WindowManagerwm = (WindowManager) getApplicationContext().getSystemService("window");// 設定LayoutParams(全域變數)相關參數wmParams = new WindowManager.LayoutParams();wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;// 該類型提供與使用者互動,置於所有應用程式上方,但是在狀態列後面wmParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;// 不接受任何按鍵事件wmParams.gravity = Gravity.LEFT | Gravity.TOP; // 調整懸浮視窗至左上方// 以螢幕左上方為原點,設定x、y初始值wmParams.x = 0;wmParams.y = 0;// 設定懸浮視窗長寬資料wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;wmParams.format = PixelFormat.RGBA_8888;wm.addView(view, wmParams);
上面這段代碼就可以放置一個view在螢幕上,這個view的生命週期不依賴於application本身。如需刪除view則調用removeView即可。
如需不斷更改view內容,位置等,可以不斷調用updateViewLayout。
以下這段主要說明如何讓view和使用者互動事件:
view.setOnTouchListener(new OnTouchListener() {public boolean onTouch(View v, MotionEvent event) {// 擷取相對螢幕的座標,即以螢幕左上方為原點x = event.getRawX();// 25是系統狀態列的高度,也可以通過方法得到準確的值,自己微調就是了y = event.getRawY() - 25;switch (event.getAction()) {case MotionEvent.ACTION_DOWN:// 擷取相對View的座標,即以此View左上方為原點mTouchStartX = event.getX();mTouchStartY = event.getY() + view.getHeight() / 2;firstX = (int) event.getX();totalMove = 0;firstDown = false;return false;case MotionEvent.ACTION_MOVE:updateViewPosition();if (firstDown) {totalMove = 0;firstDown = false;}curX = (int) event.getX();int delatX = curX - firstX;totalMove += delatX;firstX = curX;return false;case MotionEvent.ACTION_UP:updateViewPosition();mTouchStartX = mTouchStartY = 0;boolean result = false;if (totalMove > 10) {totalMove = 0;// things you shouold do hereresult = true;}if (totalMove < 0 && Math.abs(totalMove) > 10) {totalMove = 0;// things you shouold do hereresult = true;}return result;}// 寫這個是為了讓點擊和touch事件都起作用 return v.onTouchEvent(event);}});view.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(TopFloatService.this, "ok", Toast.LENGTH_SHORT).show();soundpool.play(mSound_0, 1, 1, 0, 0, 1);}}); private void updateViewPosition() { // 更新浮動視窗位置參數 wmParams.x = (int) (x - mTouchStartX); wmParams.y = (int) (y - mTouchStartY); wm.updateViewLayout(view, wmParams); }
這樣就可以接受使用者點擊,觸摸事件。上述可能有一些變數沒有聲明,可以參考如下的聲明,也可以自己聲明:
View view;SoundPool soundpool = null;private float mTouchStartX;private float mTouchStartY;private float x;private float y;int firstX, curX;private int totalMove = 0;private boolean firstDown = true;int mSound_0 = 0;Intent i;ImageView imageview;String path;
此外還可以不斷對view進行變換,比如給view設定一個動畫,或者更換布局,比如案頭歌詞的顯示與控制等都可以通過這種方式實現。
最後還是別忘記添加許可權:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />