创建一个Window是很简单的事,只需要通过WindowManager即可完成。WindowManager是外界访问Window的入口。使用WindowManager可以在其他应用最上层,甚至手机桌面最上层显示窗口。通过(WindowManager)getApplicationContext().getSystemService(Context.WINDOW_SERVICE); 即可获得WindowManager对象,调用的是WindowManager继承自基类的addView方法和removeView方法来显示和隐藏窗口。
下面是Android Window悬浮窗的一个小例子,类似于360手机桌面加速球,该加速球可以在桌面拖动,点击后开始转动(此时可以进行如清理垃圾的操作)。
package com.hongri.recyclerview.activity;
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.view.Gravity;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.Toast;
import com.hongri.recyclerview.MyApplication;
import com.hongri.recyclerview.R;
import com.hongri.recyclerview.fragment.HomeFragment;
import com.hongri.recyclerview.utils.Logger;
import com.hongri.recyclerview.widget.FloatView;
/**
* @author:zhongyao
* @description:主界面Activity
*/
public class MainActivity extends BaseActivity {
private WindowManager mWindowManager;
private WindowManager.LayoutParams param;
private ImageView mLayout;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case FloatView.CLICK:
Logger.d("收到click,开始清理垃圾");
//属性动画--旋转
Animator animator = AnimatorInflater.loadAnimator(MainActivity.this,R.animator.property_animator);
animator.setTarget(mLayout);
animator.start();
break;
}
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Logger.d("MainActivity--onCreate()");
setContentView(R.layout.activity_main);
/**
*Android6.0以上系统增加了权限管理,所以需要添加如下代码,来让用户选择打开桌面浮窗的权限
*/
if (Build.VERSION.SDK_INT >= 23) {
if (! Settings.canDrawOverlays(MainActivity.this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent,10);
}
}
showView();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 10) {
if (Build.VERSION.SDK_INT >= 23) {
if (!Settings.canDrawOverlays(this)) {
// SYSTEM_ALERT_WINDOW permission not granted...
Toast.makeText(MainActivity.this, "not granted", Toast.LENGTH_SHORT);
}
}
}
}
private void showView(){
mLayout=new FloatView(getApplicationContext(),mHandler);
mLayout.setBackgroundResource(R.drawable.sun);
//获取WindowManager
mWindowManager=(WindowManager)getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
//设置LayoutParams(全局变量)相关参数
param = ((MyApplication)getApplication()).getMywmParams();
param.type=WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;// 系统提示类型,重要(These windows are always on top of application windows)
param.format=1;
param.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; // 表示Window不需要获取焦点
param.flags = param.flags | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;//可以监听MotionEvent的ACTION_OUTSIDE事件
param.flags = param.flags | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; // 排版限制--即允许在可见的屏幕之外
param.alpha = 1.0f;
param.gravity=Gravity.LEFT|Gravity.TOP; //调整悬浮窗口至左上角
//以屏幕左上角为原点,设置x、y初始值
param.x=0;
param.y=0;
//设置悬浮窗口长宽数据
param.width=140;
param.height=140;
//显示myFloatView图像
mWindowManager.addView(mLayout, param);
}
@Override
public void onDestroy(){
super.onDestroy();
//在程序退出(Activity销毁)时销毁悬浮窗口
mWindowManager.removeView(mLayout);
}
}
package com.hongri.recyclerview.widget;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.widget.ImageView;
import com.hongri.recyclerview.MyApplication;
import com.hongri.recyclerview.utils.Logger;
/**
* 自定义桌面悬浮image,添加拖动和点击事件
*/
public class FloatView extends ImageView {
private float mTouchStartX;
private float mTouchStartY;
private float x;
private float y;
private float beginX,endX,beginY,endY;
private WindowManager wm=(WindowManager)getContext().getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
private WindowManager.LayoutParams wmParams = ((MyApplication)getContext().getApplicationContext()).getMywmParams();
private Context context;
private Handler mHandler;
public static final int CLICK = 1;
public FloatView(Context context, Handler mHandler) {
super(context);
this.context = context;
this.mHandler = mHandler;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//获取相对屏幕的坐标,即以屏幕左上角为原点
x = event.getRawX();
y = event.getRawY()/*-25*/; //25是系统状态栏的高度
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//获取相对View的坐标,即以此View左上角为原点
beginX = endX = mTouchStartX = event.getX();
beginY = endY = mTouchStartY = event.getY();
Logger.d("ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
updateViewPosition();
Logger.d("ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
// updateViewPosition();
Logger.d("ACTION_UP");
endX = event.getX();
endY = event.getY();
if (endX - beginX == 0 && endY - beginY == 0){
//点击事件
Logger.d("点击事件");
Message msg = Message.obtain();
msg.what = CLICK;
msg.obj = "点击事件";
mHandler.sendMessage(msg);
}else {
//发生了拖拽
Logger.d("发生了拖拽");
}
mTouchStartX=mTouchStartY=0;
break;
case MotionEvent.ACTION_OUTSIDE:
Logger.d("ACTION_OUTSIDE");
break;
}
return true;
}
private void updateViewPosition(){
//更新浮动窗口位置参数
wmParams.x=(int)( x-mTouchStartX);
wmParams.y=(int) (y-mTouchStartY);
Logger.d("x:"+x+" y:"+y);
Logger.d("mTouchStartX:"+mTouchStartX+" mTouchStartY:"+mTouchStartY);
wm.updateViewLayout(this, wmParams);
}
}
public class MyApplication extends Application {
private WindowManager.LayoutParams wmParams=new WindowManager.LayoutParams();
public WindowManager.LayoutParams getMywmParams(){
return wmParams;
}
}
res/animator文件下的property_animator.xml(属性动画--旋转):
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="sequentially">
<set android:ordering="together">
<objectAnimator
android:duration="800"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="1080"
android:valueType="floatType">
</objectAnimator>
</set>
</set>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
还有一点需要注意的是在6.0以上的系统中需要用户自己打开“显示悬浮窗”权限才能展示该应用的悬浮窗。