创建顶级窗口的步骤:
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代码:
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>