Android顶级窗口WindowManager

创建顶级窗口的步骤:

1.创建WindowManger

     WindowManger本身就是一个接口类型,通过getSystemService(Context.WINDOW_SERVICE)可以获得,他继承了ViewManger。

     ViewManger只有三个方法:

     public void addView(View view, ViewGroup.LayoutParams params);                  //添加一个View
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);   //更新View
    public void removeView(View view);                      //移除View


2.创建WindowManager.LayoutParams

    用于设置窗口的参数:

     WindowManager.LayoutParams.x;         如果gravity没有设置,则为屏幕x轴的位置,如果设置了gravity,则为到特定x轴距离;

举个例子,WindowManager.LayoutParams.x = 50 如果gravity设置为content,则,窗口在居中的情况下,再向x抽移动50个像素。
WindowManager.LayoutParams.y;         屏幕y轴的位置
WindowManager.LayoutParams.width;      窗口的宽度
WindowManager.LayoutParams.height;    窗口的高度
WindowManager.LayoutParams.gravity;    相对与屏幕的排版
WindowManager.LayoutParams.flags;     标记,可以设置窗口是否有焦点...

更多可以参考《WindowManager.LayoutParams详解 

3.获取View对象。

4.设置View的控件事件,如按钮的点击事件。

5.WindowManger.addView(View view, ViewGroup.LayoutParams params);显示窗口。


还有一点值得注意:

1.使用顶级窗口模式,还得添加权限:<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

2.出现不显示浮动窗口的情况,请在软件的权限中查看是否被禁用。(现在的android系统默认是禁用的。)

   

效果图:


java代码:

代码挺多的,这里的按钮都是TextView实现的,之所以不用Button,是因为Button的点击事件会截断父容器View(mView)的触摸焦点。
所以当我用Button的时候,遇到了这样的问题,当我按下按钮后,只能响应按钮的点击事件,而不能移动窗口。所以索性把所有的Button控件换成了TextView,使用触摸来模拟点击事件。还有一点就是要想在窗口中点击Edittext后,弹出虚拟键盘,还得让窗口获取焦点,也就是设置 WindowManager.LayoutParams.flags=0。
public class WindowService extends Service {

	private WindowManager mWm = null;
	private WindowManager.LayoutParams mParams;
	private View mView = null;
	private float mStateBarHeigh = 25;
	private TextView mCalculateBtn, mEditBtn, mCloseBtn, mClearBtn, mShrinkBtn,
			mExpandBtn;
	private LinearLayout mExpandLL, mMainLL;
	private EditText mEt1, mEt2;
	private TextView mTv1;

	private BtnFlag btnFlag;

	enum BtnFlag {
		calculate, move, edit, close, clear, shrink, expand;
	}

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}

	@Override
	public void onCreate() {
		mStateBarHeigh = getStatusBarHeight();
		createWindow();

	}

	@SuppressLint("NewApi")
	private void createWindow() {
		mParams = new WindowManager.LayoutParams();
		
		mWm = (WindowManager) getApplication().getSystemService(
				Context.WINDOW_SERVICE);
		mView = View.inflate(this, R.layout.activity_window, null);

		// 设置window type
		mParams.type = LayoutParams.TYPE_PHONE;
		// 设置图片格式,效果为背景透明
		mParams.format = PixelFormat.RGBA_8888;
		// 设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作)
		mParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE;
		// 调整悬浮窗显示的停靠位置为左侧置顶
		mParams.gravity = Gravity.LEFT | Gravity.TOP;
		// 以屏幕左上角为原点,设置x、y初始值,相对于gravity
		mParams.x = 50;
		mParams.y = 0;

		// 设置悬浮窗口长宽数据
		mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
		mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
		mWm.addView(mView, mParams);

		mEt1 = (EditText) mView.findViewById(R.id.num1_et);
		mEt2 = (EditText) mView.findViewById(R.id.num2_et);
		
		mTv1 = (TextView) mView.findViewById(R.id.result_tv);

		mCalculateBtn = (TextView) mView.findViewById(R.id.calculate_btn);
		mEditBtn = (TextView) mView.findViewById(R.id.edit_btn);
		mCloseBtn = (TextView) mView.findViewById(R.id.close_btn);
		mClearBtn = (TextView) mView.findViewById(R.id.clear_btn);
		mShrinkBtn = (TextView) mView.findViewById(R.id.shrink_btn);
		mExpandBtn = (TextView) mView.findViewById(R.id.expand_btn);

		mExpandLL = (LinearLayout) mView.findViewById(R.id.expand_ll);
		mMainLL = (LinearLayout) mView.findViewById(R.id.main_ll);

		mEt1.addTextChangedListener(textWatcher);
		mEt2.addTextChangedListener(textWatcher);

		mView.setOnTouchListener(listener);
		mCalculateBtn.setOnTouchListener(btnTouchListener);
		mEditBtn.setOnTouchListener(btnTouchListener);
		mCloseBtn.setOnTouchListener(btnTouchListener);
		mClearBtn.setOnTouchListener(btnTouchListener);
		mShrinkBtn.setOnTouchListener(btnTouchListener);
		mExpandBtn.setOnTouchListener(btnTouchListener);

		mEt1.setOnTouchListener(etTouchListener);
		mEt2.setOnTouchListener(etTouchListener);

	}

	private boolean isFocusable = false;

	@Override
	public void onDestroy() {
		if (mWm != null && mView != null) {
			mWm.removeView(mView);
		}
	}

	private String compare(double num1, double num2) {
		double result1 = 0;
		double result2 = 0;
		result1 = (num1 - num2) * 0.618 + num2;
		result2 = (num1 - num2) * 0.854 + num2;

		DecimalFormat df = new DecimalFormat("#.0000");

		StringBuffer sbuf = new StringBuffer();
		sbuf.append("(" + num1 + "-" + num2 + ")*0.618+" + num2 + "="
				+ df.format(result1));
		sbuf.append("\n");
		sbuf.append("(" + num1 + "-" + num2 + ")*0.854+" + num2 + "="
				+ df.format(result2));
		return sbuf.toString();
	}

	TextWatcher textWatcher = new TextWatcher() {
		@Override
		public void onTextChanged(CharSequence s, int start, int before,
				int count) {
			mWm.updateViewLayout(mView, mParams);
		}

		@Override
		public void beforeTextChanged(CharSequence s, int start, int count,
				int after) {
		}

		@Override
		public void afterTextChanged(Editable s) {
		}
	};
	View.OnTouchListener listener = new View.OnTouchListener() {
		private float x;
		private float y;
		private int tempX;
		private int tempY;

		private int vWidth;
		private int vHeight;
		private boolean touchFlag = false;;

		@Override
		public boolean onTouch(View v, final MotionEvent event) {

			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN: // 按下去触发
				x = event.getX();
				y = event.getY();
				tempX = (int) (event.getRawX() - x);
				tempY = (int) (event.getRawY() - y) - (int) mStateBarHeigh;
				
				vWidth = v.getWidth();
				vHeight = v.getHeight();

				System.out.println("down:" + x + ":" + y);
				System.out.println("down:" + event.getRawX() + ":"
						+ event.getRawY());

				int vPositionX = (int) (event.getRawX() - event.getX());
				int vPositionY = (int) (event.getRawY() - event.getY());
				if (event.getRawX() > vPositionX
						&& event.getRawX() < vPositionX + vWidth
						&& event.getRawY() > vPositionY
						&& event.getRawY() < vPositionY + vHeight) {
					// 点击的位置在窗口内
					mParams.flags = 0;
					System.out.println("在窗口内");
					touchFlag = false;
				} else {
					mParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE;
					System.out.println("不在窗口内");
					touchFlag = true;
				}
				mWm.updateViewLayout(mView, mParams);
				break;
			case MotionEvent.ACTION_UP:
				btnClickSelect(btnFlag);
				setBtnBackground(false);

				if (btnFlag == BtnFlag.shrink) {// 如果点击了缩小,就让它缩小后,移动到边上
					new AsyncTask<Void, Integer, Void>() {
						int positionX;
						int centerPositionX;

						@Override
						protected void onPreExecute() {
							positionX = (int) (event.getRawX() - event.getX());
							centerPositionX = positionX + mExpandBtn.getWidth()
									/ 2;
						}

						@Override
						protected Void doInBackground(Void... params) {
							if (centerPositionX > getScreenWidth() / 2) {
								// 移动到右边
								for (int i = positionX; i <= 720 - mEditBtn
										.getWidth(); i++) {
									try {
										Thread.sleep(1);
									} catch (InterruptedException e) {
										e.printStackTrace();
									}
									publishProgress(i);
								}
							} else {
								// 移动到左边
								for (int i = positionX; i >= 0; i--) {
									try {
										Thread.sleep(1);
									} catch (InterruptedException e) {
										e.printStackTrace();
									}
									publishProgress(i);
								}

							}
							return null;
						}

						@Override
						protected void onPostExecute(Void result) {

						}

						@Override
						protected void onProgressUpdate(Integer... values) {
							mParams.x = values[0];
							mWm.updateViewLayout(mView, mParams);
						}
					}.execute();
				}
				break;
			case MotionEvent.ACTION_MOVE:
				if (touchFlag) {
					break;
				}
				mParams.x = (int) (event.getRawX() - x);
				mParams.y = (int) (event.getRawY() - y) - (int) mStateBarHeigh;
				mWm.updateViewLayout(mView, mParams);
				if (Math.abs(tempX - mParams.x) > 5
						|| Math.abs(tempY - mParams.y) > 5) {
					btnFlag = BtnFlag.move;
					setBtnBackground(false);
				}

				System.out.println("XXX:" + event.getRawX() + "   YYY:"
						+ event.getRawY());
				System.out.println("宽度和高度:" + mParams.width + ":"
						+ mParams.height);
				System.out.println("宽度和高度2:" + v.getWidth() + ":"
						+ v.getHeight());
				System.out.println("宽度和高度3:" + v.getTop() + ":" + v.getWidth());
				break;
			}
			return true;
		}
	};

	private View mPressedView;
	View.OnTouchListener btnTouchListener = new View.OnTouchListener() {
		@Override
		public boolean onTouch(View v, MotionEvent event) {

			if (event.getAction() == MotionEvent.ACTION_DOWN) {
				mPressedView = v;
				setBtnBackground(true);
				switch (v.getId()) {
				case R.id.calculate_btn: // 计算
					btnFlag = BtnFlag.calculate;
					break;
				case R.id.edit_btn: // 编辑
					btnFlag = BtnFlag.edit;
					break;
				case R.id.close_btn: // 关闭
					btnFlag = BtnFlag.close;
					break;
				case R.id.clear_btn: // 清除
					btnFlag = BtnFlag.clear;
					break;
				case R.id.shrink_btn: // 缩
					btnFlag = BtnFlag.shrink;
					break;
				case R.id.expand_btn: // 展
					btnFlag = BtnFlag.expand;
					break;
				default:
					btnFlag = BtnFlag.move;
					break;
				}
			}
			return false;
		}
	};
	View.OnTouchListener etTouchListener = new View.OnTouchListener() {
		@Override
		public boolean onTouch(View v, MotionEvent event) {

			if (event.getAction() == MotionEvent.ACTION_DOWN) {
				mParams.flags = 0;
				mWm.updateViewLayout(mView, mParams);
				v.setFocusable(true);
				v.setFocusableInTouchMode(true);
				v.requestFocus();
				InputMethodManager inputManager = (InputMethodManager) getApplication()
						.getSystemService(Context.INPUT_METHOD_SERVICE);
				inputManager.showSoftInput(v, 0);
			}
			return false;
		}
	};

	private void btnClickSelect(BtnFlag btnFlag) {
		if (btnFlag == BtnFlag.calculate) {// 计算
			calculate();
		} else if (btnFlag == BtnFlag.edit) {// 编辑
			edit();
		} else if (btnFlag == BtnFlag.close) {// 关闭
			close();
		} else if (btnFlag == BtnFlag.clear) {// 清除
			clear();
		} else if (btnFlag == BtnFlag.shrink) {// 缩
			shrink();
		} else if (btnFlag == BtnFlag.expand) {// 展
			expand();
		}
	}

	private void setBtnBackground(boolean isPressed) {
		if (mPressedView == null) {
			return;
		}
		if (isPressed) {
			mPressedView.setBackgroundColor(getResources().getColor(
					R.color.btn_pressed));
		} else {
			mPressedView.setBackgroundColor(getResources().getColor(
					R.color.btn_normal));
		}
	}

	private void calculate() {
		double num1 = 0;
		double num2 = 0;
		if (mEt1 != null && !mEt1.getText().toString().trim().equals("")) {
			num1 = Double.parseDouble(mEt1.getText().toString());
		} else {
			Toast.makeText(WindowService.this, "数字1不能为空", Toast.LENGTH_LONG)
					.show();
			return;
		}
		if (mEt2 != null && !mEt2.getText().toString().trim().equals("")) {
			num2 = Double.parseDouble(mEt2.getText().toString());
		} else {
			Toast.makeText(WindowService.this, "数字2不能为空", Toast.LENGTH_LONG)
					.show();
			return;
		}

		String resultStr = compare(num1, num2);
		mTv1.setText("结果:\n" + resultStr);
	}

	private void edit() {
		isFocusable = !isFocusable;
		// 取消焦点
		if (isFocusable) {
			mParams.flags = 0;
			mEditBtn.setText("编辑(有效)");
		} else {
			mParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE;
			mEditBtn.setText("编辑(无效)");
		}
		mWm.updateViewLayout(mView, mParams);
	}

	private void close() {
		Intent intent = new Intent(WindowService.this, WindowService.class);
		stopService(intent);
	}

	private void clear() {
		mEt1.setText("");
		mEt2.setText("");
		mTv1.setText("");
		mWm.updateViewLayout(mView, mParams);
	}

	private void shrink() {
		mMainLL.setVisibility(View.GONE);
		mExpandLL.setVisibility(View.VISIBLE);
		mParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE;
		mWm.updateViewLayout(mView, mParams);
	}

	private void expand() {
		mMainLL.setVisibility(View.VISIBLE);
		mExpandLL.setVisibility(View.GONE);
		mWm.updateViewLayout(mView, mParams);
	}

	/**
	 * 获取状态栏高度
	 * 
	 * @return
	 */
	public int getStatusBarHeight() {
		Class<?> c = null;
		Object obj = null;
		java.lang.reflect.Field field = null;
		int x = 0;
		int statusBarHeight = 0;
		try {
			c = Class.forName("com.android.internal.R$dimen");
			obj = c.newInstance();
			field = c.getField("status_bar_height");
			x = Integer.parseInt(field.get(obj).toString());
			statusBarHeight = getResources().getDimensionPixelSize(x);
			return statusBarHeight;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return statusBarHeight;
	}

	public int getScreenWidth(){
		DisplayMetrics sm = new DisplayMetrics();
		mWm.getDefaultDisplay().getMetrics(sm);
		return sm.widthPixels;
	}
}

布局代码:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent"
    android:orientation="vertical"
     >

    <LinearLayout
        android:id="@+id/main_ll"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#019CEC"
        android:orientation="vertical" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:paddingLeft="5dp"
            android:paddingRight="5dp"
            android:paddingTop="10dp"
            android:paddingBottom="2dp" >

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:gravity="center_vertical"
                android:text="数字1:"
                android:textColor="@color/font_color" />

            <EditText
                android:id="@+id/num1_et"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:inputType="numberDecimal"
                android:minEms="6"
                android:padding="5dp"
                android:gravity="center_vertical"
                android:textColor="@color/font_color"
                android:background="@drawable/edit_bg_border"
                 />
        </LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:paddingLeft="5dp"
            android:paddingRight="5dp"
            android:paddingTop="2dp"
            android:paddingBottom="2dp"  >
             <TextView
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:gravity="center_vertical"
                android:text="数字2:"
                android:textColor="@color/font_color" />

            <EditText
                android:id="@+id/num2_et"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:inputType="numberDecimal"
                android:minEms="6"
                android:padding="5dp"
                android:gravity="center_vertical"
                android:background="@drawable/edit_bg_border" 
                android:textColor="@color/font_color"
                 />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:padding="10dp" >

            <TextView
                android:id="@+id/result_tv"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:gravity="center_vertical"
                android:textColor="@color/font_color" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            >

            <TextView
                style="@style/TextViewStyle"
                android:id="@+id/calculate_btn"
                android:layout_weight="1"
                android:text="计算"
                 />

            <TextView
                style="@style/TextViewStyle"
                android:id="@+id/edit_btn"
                android:layout_weight="2"
                android:text="编辑(无效)"
                android:visibility="gone"
			/>
            
			<TextView
                style="@style/TextViewStyle"
                android:id="@+id/clear_btn"
                android:layout_weight="1"
                android:text="清除"
                />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
             >

            <TextView
                style="@style/TextViewStyle"
                android:id="@+id/close_btn"
                android:layout_weight="1"
                android:text="关闭"
                />

            <TextView
                style="@style/TextViewStyle"
                android:id="@+id/shrink_btn"
                android:layout_weight="1"
                android:text="缩"
                />
        </LinearLayout>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/expand_ll"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/transparent"
        android:orientation="vertical"
        android:visibility="gone"
        >
        <TextView
            android:id="@+id/expand_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="show"
            android:textColor="@color/font_color"
            style="@style/TextViewStyle"
            android:layout_margin="0dp"
            />
    </LinearLayout>

</LinearLayout>



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值