Android 悬浮窗口
Android 懸浮窗口
一.創建懸浮窗口步驟
??? 1.實現一個ViewGroup類,作為懸浮窗口的界面類,以便在里面重寫onInterceptTouchEvent和onTouchEvent方法,實現移動界面的目的.
????? 在本例中實現了一個FloatLayer類,可以作為通用的類,使用時需要傳入WindowManager對象以實現移動窗口.
// FloatLayer ~ package com.example.hellofloatingwnd;import static com.ahai.util.DebugMessage.d; import android.content.Context; import android.graphics.PixelFormat; import android.util.AttributeSet; import android.view.Gravity; import android.view.MotionEvent; import android.view.WindowManager; import android.widget.RelativeLayout;public class FloatLayer extends RelativeLayout {// flags: 須設置成 FLAG_NOT_FOCUSABLE, 否則懸浮窗口下面的窗口不能取得焦點, 無法響應觸摸事件// type: 值低的窗口在值高的下層,相同的 type值,后創建的窗口顯示在先創建的窗口上面.// 對應的type需要相應的權限,否則會報異常 BadTokenException.// 對于權限 android.permission.SYSTEM_ALERT_WINDOW 可使用以下幾個值:// TYPE_PHONE, TYPE_SYSTEM_ALERT, TYPE_TOAST, TYPE_SYSTEM_OVERLAY// 其中 TYPE_TOAST, TYPE_SYSTEM_OVERLAY 不能響應觸摸事件public static class FloatLayoutParams extends WindowManager.LayoutParams {public FloatLayoutParams() {super(TYPE_PHONE, FLAG_NOT_FOCUSABLE, PixelFormat.RGBA_8888);gravity = Gravity.LEFT | Gravity.TOP;}public FloatLayoutParams(int type) {super(type, FLAG_NOT_FOCUSABLE, PixelFormat.RGBA_8888);gravity = Gravity.LEFT | Gravity.TOP;}public FloatLayoutParams(int xpos, int ypos) {super(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, xpos,ypos, TYPE_PHONE, FLAG_NOT_FOCUSABLE, PixelFormat.RGBA_8888);gravity = Gravity.LEFT | Gravity.TOP;}public FloatLayoutParams(int xpos, int ypos, int type) {super(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, xpos,ypos, type, FLAG_NOT_FOCUSABLE, PixelFormat.RGBA_8888);gravity = Gravity.LEFT | Gravity.TOP;}}private WindowManager mWindowManager;private int mStatusBarHeight;private int mMoveX;private int mMoveY;public FloatLayer(Context context, AttributeSet attrs, int defStyleAttr,int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);mStatusBarHeight = getStatusBarHeight();}public FloatLayer(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mStatusBarHeight = getStatusBarHeight();}public FloatLayer(Context context, AttributeSet attrs) {super(context, attrs);mStatusBarHeight = getStatusBarHeight();}public FloatLayer(Context context) {super(context);mStatusBarHeight = getStatusBarHeight();}public void setWindowManager(WindowManager windowManager) {mWindowManager = windowManager;}/** 取得系統狀態欄的高度 */private int getStatusBarHeight() {int statusBarHeight = 0;int resourceId = getResources().getIdentifier("status_bar_height","dimen", "android");if (resourceId > 0) {statusBarHeight = getResources().getDimensionPixelSize(resourceId);}// d("statusBarHeight=" + statusBarHeight);return statusBarHeight;}@Overridepublic boolean onInterceptTouchEvent(MotionEvent event) {final int action = event.getAction();if (action == MotionEvent.ACTION_MOVE) {if (handleMoveEvent(event))return true;} else if (action == MotionEvent.ACTION_DOWN) {mMoveX = (int) event.getRawX();mMoveY = (int) event.getRawY();}return super.onInterceptTouchEvent(event);}@Overridepublic boolean onTouchEvent(MotionEvent event) {final int action = event.getAction();if (action == MotionEvent.ACTION_MOVE) {if (handleMoveEvent(event))return true;} else if (action == MotionEvent.ACTION_DOWN) {mMoveX = (int) event.getRawX();mMoveY = (int) event.getRawY();}return super.onTouchEvent(event);}private boolean handleMoveEvent(MotionEvent event) {try {if (mWindowManager != null) {// 通過以下消息可知getLayoutParams得到的對象即為 addView 傳入的 LayoutParams 對象// d("class:" + getLayoutParams().getClass());final int x = (int) event.getRawX();final int y = (int) event.getRawY();int[] location = new int[2];getLocationOnScreen(location);FloatLayoutParams layoutParams = (FloatLayoutParams) getLayoutParams();layoutParams.x = location[0] + (x - mMoveX);layoutParams.y = location[1] + (y - mMoveY) - mStatusBarHeight;mWindowManager.updateViewLayout(this, layoutParams);mMoveX = x;mMoveY = y;return true;}} catch (Exception e) {d("", e);}return false;} }
??? 2.在res/layout中創建一個布局文件,實現界面布局.如float_layout.xml
<?xml version="1.0" encoding="utf-8"?> <com.example.hellofloatingwnd.FloatLayer xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="wrap_content" ><ImageButtonandroid:id="@+id/mBtnHide"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentTop="true"android:layout_centerHorizontal="true"android:background="@drawable/hide_button_selector" /><TextViewandroid:id="@+id/mTvHello"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/mBtnHide"android:layout_centerHorizontal="true"android:text="@string/hello"android:textColor="#21d" /></com.example.hellofloatingwnd.FloatLayer>
????
??? 3.取得WindowManager對象.
??????? 在 Activity 中可以通過以下方法取得, 其中前面的3個方法實際取得的是當前Activity的應用程序窗口對象,在Activity銷毀等情況下,
??? WindowManager對象也就不存在了,需要將懸浮窗口移除,否則會報錯.
??????? WindowManager windowManager;
??????? windowManager = getWindow().getWindowManager();
??????? windowManager = getWindowManager();
??????? windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
??????? windowManager = (WindowManager) getApplication().getSystemService(WINDOW_SERVICE);
??????? 在 Service 中, 以下兩個方法均可, 在 onDestory 中將懸浮窗口移除即可.
??????? windowManager = (WindowManager) getApplication().getSystemService(WINDOW_SERVICE);
??????? windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
???
??? 4.創建View并顯示. 通過以下兩個行代碼完成:
??????? mFloatView = (FloatLayer) inflater.inflate(R.layout.float_layout, null);
??????? windowManager.addView(mFloatView, layoutParams);
??????? 其中layoutParams是實現懸浮窗口的關鍵,窗口的配置及移動都通過其指定.
??????? LayoutParams需使用WindowManager.LayoutParams對象或繼承自該類重寫.
??????? 移動窗口通過下面的代碼實現:
??????????? mWindowManager.updateViewLayout(mFloatView, layoutParams);
??????? layoutParams最重要的兩個參數是flags和type:
??????? flags: 須設置成 FLAG_NOT_FOCUSABLE, 否則懸浮窗口下面的窗口不能取得焦點,不能響應觸摸事件.
??????? type: 值低的窗口在值高的下層,相同的 type值,后創建的窗口顯示在先創建的窗口上面.
????????????? 對應的type需要相應的權限,否則會報異常 BadTokenException.
??????? 對于權限 android.permission.SYSTEM_ALERT_WINDOW, type可使用以下幾個值:
??????????? TYPE_PHONE, TYPE_SYSTEM_ALERT, TYPE_TOAST, TYPE_SYSTEM_OVERLAY
??????? 其中 TYPE_TOAST, TYPE_SYSTEM_OVERLAY 不能響應觸摸事件
在Service中實現
package com.example.hellofloatingwnd;import android.app.Application; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.WindowManager; import android.widget.ImageButton; import android.widget.Toast;public class FloatingService extends Service {private FloatLayer mFloatView;private WindowManager mWindowManager;private ImageButton mBtnHide;@Overridepublic void onCreate() {super.onCreate();Application app = getApplication();mWindowManager = (WindowManager) app.getSystemService(WINDOW_SERVICE);LayoutInflater inflater = LayoutInflater.from(app);mFloatView = (FloatLayer) inflater.inflate(R.layout.float_layout, null);mFloatView.setWindowManager(mWindowManager);mBtnHide = (ImageButton) mFloatView.findViewById(R.id.mBtnHide);mBtnHide.setOnClickListener(mClickListener);FloatLayer.FloatLayoutParams layoutParams;layoutParams = new FloatLayer.FloatLayoutParams(10, 100);mWindowManager.addView(mFloatView, layoutParams);}@Overridepublic void onDestroy() {super.onDestroy();if (mFloatView != null) {mWindowManager.removeView(mFloatView);mFloatView = null;}}@Overridepublic IBinder onBind(Intent arg0) {return null;}private OnClickListener mClickListener = new OnClickListener() {@Overridepublic void onClick(View view) {if (view.getId() == R.id.mBtnHide) {Toast.makeText(getApplicationContext(),"on float button clicked.", Toast.LENGTH_SHORT).show();FloatingService.this.stopSelf();}}}; }
在Activity中實現
package com.example.hellofloatingwnd;import android.app.Activity; import android.app.Application; import android.content.Intent; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.WindowManager; import android.widget.Button; import android.widget.TextView;public class MainActivity extends Activity {private Button mBtnStart;private Button mBtnStop;private FloatLayer mFloatView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mBtnStart = (Button) findViewById(R.id.mBtnStart);mBtnStop = (Button) findViewById(R.id.mBtnStop);mBtnStart.setOnClickListener(mClickListener);mBtnStop.setOnClickListener(mClickListener);WindowManager windowManager;// windowManager = getWindow().getWindowManager();windowManager = getWindowManager();// windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);// windowManager = (WindowManager)// getApplication().getSystemService(WINDOW_SERVICE); Application application = getApplication();LayoutInflater inflater = LayoutInflater.from(application);mFloatView = (FloatLayer) inflater.inflate(R.layout.float_layout, null);mFloatView.setWindowManager(windowManager);TextView textView = (TextView) mFloatView.findViewById(R.id.mTvHello);textView.setText("This create by activity.");FloatLayer.FloatLayoutParams layoutParams;layoutParams = new FloatLayer.FloatLayoutParams(50, 200);windowManager.addView(mFloatView, layoutParams);}@Overrideprotected void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();if (mFloatView != null) {WindowManager windowManager = getWindow().getWindowManager();windowManager.removeView(mFloatView);mFloatView = null;}}private OnClickListener mClickListener = new OnClickListener() {@Overridepublic void onClick(View view) {if (view.getId() == R.id.mBtnStart) {Intent intent = new Intent(MainActivity.this,FloatingService.class);startService(intent);} else if (view.getId() == R.id.mBtnStop) {Intent intent = new Intent(MainActivity.this,FloatingService.class);stopService(intent);}}}; }
???????
??? 5.在AndroidManifest.xml文件中添加權限,在layoutParams中配置不同的type類型需要不同的權限.
??????? <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
???
轉載于:https://www.cnblogs.com/diysoul/p/5169234.html
總結
以上是生活随笔為你收集整理的Android 悬浮窗口的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SpringMVC工作环境搭建 配置文件
- 下一篇: 工地上工伤九级大约能赔多少钱?