Android 之 悬浮窗口

1. 创建并设置  WindowManager  类

		WindowManager mWindowManager;
		// 取得系统窗体
		mWindowManager = (WindowManager) getApplicationContext()
				.getSystemService("window");

		// 窗体的布局样式
		mLayout = new WindowManager.LayoutParams();

		// 设置窗体显示类型——TYPE_SYSTEM_ALERT(系统提示)需要添加权限
		/*
		 * AndroidManifest.xml 文件
		 * <!-- 添加权限-->
		    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
		 * */
		//mLayout.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
		
		//设置窗体显示类型——TYPE_SYSTEM_ALERT(系统提示)  不需要添加权限
		mLayout.type = WindowManager.LayoutParams.TYPE_TOAST;

		// 设置窗体焦点及触摸:
		// FLAG_NOT_FOCUSABLE(不能获得按键输入焦点)
		mLayout.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;

		// 设置显示的模式
		mLayout.format = PixelFormat.RGBA_8888;

		// 设置对齐的方法
		mLayout.gravity = Gravity.TOP | Gravity.LEFT;

		// 设置窗体宽度和高度
		mLayout.width = WindowManager.LayoutParams.WRAP_CONTENT;
		mLayout.height = WindowManager.LayoutParams.WRAP_CONTENT;

  2. 创建并设置view 类 (TextView  为例, 主要是显示的view, 监听了两个事件, 一个为拖动事件另一个为双击移除悬浮)

TextView mDesktopLayout = new TextView(this);
		
		mDesktopLayout.setOnTouchListener(new OnTouchListener() {
			float mTouchStartX;
			float mTouchStartY;

			@Override
			public boolean onTouch(View v, MotionEvent event) {
				// 获取相对屏幕的坐标,即以屏幕左上角为原点
				x = event.getRawX();
				y = event.getRawY() - top; // 25是系统状态栏的高度
				Log.i("startP", "startX" + mTouchStartX + "====startY"
						+ mTouchStartY);
				switch (event.getAction()) {
				case MotionEvent.ACTION_DOWN:
					// 获取相对View的坐标,即以此View左上角为原点
					mTouchStartX = event.getX();
					mTouchStartY = event.getY();
					Log.i("startP", "startX" + mTouchStartX + "====startY"
							+ mTouchStartY);
					long end = System.currentTimeMillis() - startTime;
					// 双击的间隔在 300ms以下
					if (end < 300) {
						closeDesk();
					}
					startTime = System.currentTimeMillis();
					break;
				case MotionEvent.ACTION_MOVE:
					// 更新浮动窗口位置参数
					mLayout.x = (int) (x - mTouchStartX);
					mLayout.y = (int) (y - mTouchStartY);
					mWindowManager.updateViewLayout(v, mLayout);
					break;
				case MotionEvent.ACTION_UP:

					// 更新浮动窗口位置参数
					mLayout.x = (int) (x - mTouchStartX);
					mLayout.y = (int) (y - mTouchStartY);
					mWindowManager.updateViewLayout(v, mLayout);

					// 可以在此记录最后一次的位置

					mTouchStartX = mTouchStartY = 0;
					break;
				}
				return true;
			}
		});

  3. 添加和移除悬浮

	/**
	 * 显示DesktopLayout
	 */
	private void showDesk() {
		
		mDesktopLayout.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
		mDesktopLayout.setText("test0000001");
		mWindowManager.addView(mDesktopLayout, mLayout);
		//finish();
	}

	/**
	 * 关闭DesktopLayout
	 */
	private void closeDesk() {
		mWindowManager.removeView(mDesktopLayout);
		//finish();
	}

  思路总结: 利用系统弹窗口置顶的原理实现悬浮, 系统弹出窗口使用了WindowManager类, 然后利用该类的addView方法把view视图类添加进去, 利用removeView方法把添加进去的类移除, 利用updateViewLayout方法更新窗口数据如位置等.

附加:

1.变量声明

	private WindowManager mWindowManager;
	private WindowManager.LayoutParams mLayout;
	private TextView mDesktopLayout;
	//private DesktopLayout mDesktopLayout;
	private long startTime;
	// 声明屏幕的宽高
	float x, y;
	int top;

2.WindowManager  的  type属性取值情况

a.  WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; 系统错误提示窗口, 需要权限, 视图显示在所有窗口前面, 包括锁屏.

b. WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; 系统对话框窗口, 需要权限, 视图显示在窗口前面, 不包括锁屏, 状态栏下拉面板.

c. WindowManager.LayoutParams.TYPE_TOAST; 提示窗口, 不需要权限, 和TYPE_SYSTEM_ALERT显示范围一样.

原文地址:https://www.cnblogs.com/weloglog888/p/6298172.html