现在很多安全类的软件,比如360手机助手,百度手机助手等等,都有一个悬浮窗,可以飘浮在桌面上,方便用户使用一些常用的操作。
今天这篇文章,就是介绍如何实现桌面悬浮窗效果的。
首先,看一下效果图。
悬浮窗一共分为两个部分,一个是平常显示的小窗口,另外一个是点击小窗口显示出来的二级悬浮窗口。
首先,先看一下这个项目的目录结构。
最关键的就是红框内的四个类。
首先,FloatWindowService是一个后台的服务类,主要负责在后台不断的刷新桌面上的小悬浮窗口,否则会导致更换界面之后,悬浮窗口也会随之消失,因此需要不断的刷新。下面是实现代码。
package com.qust.floatwindow;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
/**
* 悬浮窗后台服务
*
* @author zhaokaiqiang
*
*/
public class FloatWindowService extends Service {
public static final String LAYOUT_RES_ID = "layoutResId";
public static final String ROOT_LAYOUT_ID = "rootLayoutId";
// 用于在线程中创建/移除/更新悬浮窗
private Handler handler = new Handler();
private Context context;
private Timer timer;
// 小窗口布局资源id
private int layoutResId;
// 布局根布局id
private int rootLayoutId;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
context = this;
layoutResId = intent.getIntExtra(LAYOUT_RES_ID, 0);
rootLayoutId = intent.getIntExtra(ROOT_LAYOUT_ID, 0);
if (layoutResId == 0 || rootLayoutId == 0) {
throw new IllegalArgumentException(
"layoutResId or rootLayoutId is illegal");
}
if (timer == null) {
timer = new Timer();
// 每500毫秒就执行一次刷新任务
timer.scheduleAtFixedRate(new RefreshTask(), 0, 500);
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
// Service被终止的同时也停止定时器继续运行
timer.cancel();
timer = null;
}
private class RefreshTask extends TimerTask {
@Override
public void run() {
// 当前界面没有悬浮窗显示,则创建悬浮
if (!FloatWindowManager.getInstance(context).isWindowShowing()) {
handler.post(new Runnable() {
@Override
public void run() {
FloatWindowManager.getInstance(context)
.createSmallWindow(context, layoutResId,
rootLayoutId);
}
});
}
}
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
除了后台服务之外,我们还需要两个自定义的布局,分别是FloatWindowSmallView和FloatWindowBigView,这两个自定义的布局,主要负责悬浮窗的前台显示,我们分别看一下代码实现。
首先是FloatWindowSmallView类的实现。
package com.qust.floatwindow;
import java.lang.reflect.Field;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.qust.demo.ScreenUtils;
import com.qust.floatingwindow.R;
/**
* 小悬浮窗,用于初始显示
*
* @author zhaokaiqiang
*
*/
public class FloatWindowSmallView extends LinearLayout {
// 小悬浮窗的宽
public int viewWidth;
// 小悬浮窗的高
public int viewHeight;
// 系统状态栏的高度
private static int statusBarHeight;
// 用于更新小悬浮窗的位置
private WindowManager windowManager;
// 小悬浮窗的布局参数
public WindowManager.LayoutParams smallWindowParams;
// 记录当前手指位置在屏幕上的横坐标
private float xInScreen;
// 记录当前手指位置在屏幕上的纵坐标
private float yInScreen;
// 记录手指按下时在屏幕上的横坐标,用来判断单击事件
private float xDownInScreen;
// 记录手指按下时在屏幕上的纵坐标,用来判断单击事件
private float yDownInScreen;
// 记录手指按下时在小悬浮窗的View上的横坐标
private float xInView;
// 记录手指按下时在小悬浮窗的View上的纵坐标
private float yInView;
// 单击接口
private OnC