转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992

现在很多安全类的软件,比如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;// 小窗口布局资源idprivate int layoutResId;// 布局根布局idprivate int rootLayoutId;@Overridepublic 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);}@Overridepublic void onDestroy() {super.onDestroy();// Service被终止的同时也停止定时器继续运行timer.cancel();timer = null;}private class RefreshTask extends TimerTask {@Overridepublic void run() {// 当前界面没有悬浮窗显示,则创建悬浮if (!FloatWindowManager.getInstance(context).isWindowShowing()) {handler.post(new Runnable() {@Overridepublic void run() {FloatWindowManager.getInstance(context).createSmallWindow(context, layoutResId,rootLayoutId);}});}}}@Overridepublic 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 OnClickListener listener;/*** 构造函数* * @param context*            上下文对象* @param layoutResId*            布局资源id* @param rootLayoutId*            根布局id*/public FloatWindowSmallView(Context context, int layoutResId,int rootLayoutId) {super(context);windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);LayoutInflater.from(context).inflate(layoutResId, this);View view = findViewById(rootLayoutId);viewWidth = view.getLayoutParams().width;viewHeight = view.getLayoutParams().height;statusBarHeight = getStatusBarHeight();TextView percentView = (TextView) findViewById(R.id.percent);percentView.setText("悬浮窗");smallWindowParams = new WindowManager.LayoutParams();// 设置显示类型为phonesmallWindowParams.type = WindowManager.LayoutParams.TYPE_PHONE;// 显示图片格式smallWindowParams.format = PixelFormat.RGBA_8888;// 设置交互模式smallWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;// 设置对齐方式为左上smallWindowParams.gravity = Gravity.LEFT | Gravity.TOP;smallWindowParams.width = viewWidth;smallWindowParams.height = viewHeight;smallWindowParams.x = ScreenUtils.getScreenWidth(context);smallWindowParams.y = ScreenUtils.getScreenHeight(context) / 2;}@SuppressLint("ClickableViewAccessibility")@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {// 手指按下时记录必要的数据,纵坐标的值都减去状态栏的高度case MotionEvent.ACTION_DOWN:// 获取相对与小悬浮窗的坐标xInView = event.getX();yInView = event.getY();// 按下时的坐标位置,只记录一次xDownInScreen = event.getRawX();yDownInScreen = event.getRawY() - statusBarHeight;break;case MotionEvent.ACTION_MOVE:// 时时的更新当前手指在屏幕上的位置xInScreen = event.getRawX();yInScreen = event.getRawY() - statusBarHeight;// 手指移动的时候更新小悬浮窗的位置updateViewPosition();break;case MotionEvent.ACTION_UP:// 如果手指离开屏幕时,按下坐标与当前坐标相等,则视为触发了单击事件if (xDownInScreen == event.getRawX()&& yDownInScreen == (event.getRawY() - getStatusBarHeight())) {if (listener != null) {listener.click();}}break;}return true;}/*** 设置单击事件的回调接口*/public void setOnClickListener(OnClickListener listener) {this.listener = listener;}/*** 更新小悬浮窗在屏幕中的位置*/private void updateViewPosition() {smallWindowParams.x = (int) (xInScreen - xInView);smallWindowParams.y = (int) (yInScreen - yInView);windowManager.updateViewLayout(this, smallWindowParams);}/*** 获取状态栏的高度* * @return*/private int getStatusBarHeight() {try {Class<?> c = Class.forName("com.android.internal.R$dimen");Object o = c.newInstance();Field field = c.getField("status_bar_height");int x = (Integer) field.get(o);return getResources().getDimensionPixelSize(x);} catch (Exception e) {e.printStackTrace();}return 0;}/*** 单击接口* * @author zhaokaiqiang* */public interface OnClickListener {public void click();}}

在这个类里面,主要的工作是实现悬浮窗口在桌面前端的实现,还有就是位置的移动和单击事件的判断以及处理。这里使用的是主要是WindowManager类的一些方法和属性,下一篇会详细说明,这篇只说实现。

除了小悬浮窗之外,点击之后弹出的二级悬浮窗也是类似的方式添加到桌面上,下面是二级悬浮窗的代码。

package com.qust.floatwindow;import android.content.Context;
import android.graphics.PixelFormat;
import android.view.Gravity;
import android.view.LayoutInflater;
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;public class FloatWindowBigView extends LinearLayout {// 记录大悬浮窗的宽public int viewWidth;// 记录大悬浮窗的高public int viewHeight;public WindowManager.LayoutParams bigWindowParams;private Context context;public FloatWindowBigView(Context context) {super(context);this.context = context;LayoutInflater.from(context).inflate(R.layout.float_window_big, this);View view = findViewById(R.id.big_window_layout);viewWidth = view.getLayoutParams().width;viewHeight = view.getLayoutParams().height;bigWindowParams = new WindowManager.LayoutParams();// 设置显示的位置,默认的是屏幕中心bigWindowParams.x = ScreenUtils.getScreenWidth(context) / 2 - viewWidth/ 2;bigWindowParams.y = ScreenUtils.getScreenHeight(context) / 2- viewHeight / 2;bigWindowParams.type = WindowManager.LayoutParams.TYPE_PHONE;bigWindowParams.format = PixelFormat.RGBA_8888;// 设置交互模式bigWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;bigWindowParams.gravity = Gravity.LEFT | Gravity.TOP;bigWindowParams.width = viewWidth;bigWindowParams.height = viewHeight;initView();}private void initView() {TextView tv_back = (TextView) findViewById(R.id.tv_back);tv_back.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {FloatWindowManager.getInstance(context).removeBigWindow();}});}}

这些基本的类建立起来之后,剩下的就是最重要的类FloatWindowManager的实现。这个类实现的就是对悬浮窗的操作。

package com.qust.floatwindow;import android.content.Context;
import android.content.Intent;
import android.view.WindowManager;/*** 悬浮窗管理器* * @author zhaokaiqiang* */
public class FloatWindowManager {// 小悬浮窗对象private FloatWindowSmallView smallWindow;// 大悬浮窗对象private FloatWindowBigView bigWindow;// 用于控制在屏幕上添加或移除悬浮窗private WindowManager mWindowManager;// FloatWindowManager的单例private static FloatWindowManager floatWindowManager;// 上下文对象private Context context;private FloatWindowManager(Context context) {this.context = context;}public static FloatWindowManager getInstance(Context context) {if (floatWindowManager == null) {floatWindowManager = new FloatWindowManager(context);}return floatWindowManager;}/*** 创建小悬浮窗* * @param context*            必须为应用程序的Context.*/public void createSmallWindow(Context context, int layoutResId,int rootLayoutId) {WindowManager windowManager = getWindowManager();if (smallWindow == null) {smallWindow = new FloatWindowSmallView(context, layoutResId,rootLayoutId);windowManager.addView(smallWindow, smallWindow.smallWindowParams);}}/*** 将小悬浮窗从屏幕上移除* * @param context*/public void removeSmallWindow() {if (smallWindow != null) {WindowManager windowManager = getWindowManager();windowManager.removeView(smallWindow);smallWindow = null;}}public void setOnClickListener(FloatWindowSmallView.OnClickListener listener) {if (smallWindow != null) {smallWindow.setOnClickListener(listener);}}/*** 创建大悬浮窗* * @param context*            必须为应用程序的Context.*/public void createBigWindow(Context context) {WindowManager windowManager = getWindowManager();if (bigWindow == null) {bigWindow = new FloatWindowBigView(context);windowManager.addView(bigWindow, bigWindow.bigWindowParams);}}/*** 将大悬浮窗从屏幕上移除* * @param context*/public void removeBigWindow() {if (bigWindow != null) {WindowManager windowManager = getWindowManager();windowManager.removeView(bigWindow);bigWindow = null;}}public void removeAll() {context.stopService(new Intent(context, FloatWindowService.class));removeSmallWindow();removeBigWindow();}/*** 是否有悬浮窗显示(包括小悬浮窗和大悬浮)* * @return 有悬浮窗显示在桌面上返回true,没有的话返回false*/public boolean isWindowShowing() {return smallWindow != null || bigWindow != null;}/*** 如果WindowManager还未创建,则创建新的WindowManager返回。否则返回当前已创建的WindowManager* * @param context* @return*/private WindowManager getWindowManager() {if (mWindowManager == null) {mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);}return mWindowManager;}}

还有个获取屏幕宽高的帮助类。

package com.qust.demo;import android.content.Context;
import android.view.WindowManager;/*** 屏幕帮助类* * @author zhaokaiqiang* */
public class ScreenUtils {/*** 获取屏幕宽度* * @return*/@SuppressWarnings("deprecation")public static int getScreenWidth(Context context) {return ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getWidth();}/*** 获取屏幕宽度* * @return*/@SuppressWarnings("deprecation")public static int getScreenHeight(Context context) {return ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getHeight();}}

完成这些,我们就可以直接用了。

package com.qust.demo;import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;import com.qust.floatingwindow.R;
import com.qust.floatwindow.FloatWindowManager;
import com.qust.floatwindow.FloatWindowService;
import com.qust.floatwindow.FloatWindowSmallView.OnClickListener;/*** 示例* * @ClassName: com.qust.demo.MainActivity* @Description:* @author zhaokaiqiang* @date 2014-10-23 下午11:30:13* */
public class MainActivity extends Activity {private FloatWindowManager floatWindowManager;private Context context;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);context = this;floatWindowManager = FloatWindowManager.getInstance(context);}/*** 显示小窗口* * @param view*/public void show(View view) {// 需要传递小悬浮窗布局,以及根布局的id,启动后台服务Intent intent = new Intent(context, FloatWindowService.class);intent.putExtra(FloatWindowService.LAYOUT_RES_ID,R.layout.float_window_small);intent.putExtra(FloatWindowService.ROOT_LAYOUT_ID,R.id.small_window_layout);startService(intent);}/*** 显示二级悬浮窗* * @param view*/public void showBig(View view) {// 设置小悬浮窗的单击事件floatWindowManager.setOnClickListener(new OnClickListener() {@Overridepublic void click() {floatWindowManager.createBigWindow(context);}});}/*** 移除所有的悬浮窗* * @param view*/public void remove(View view) {floatWindowManager.removeAll();}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {// 返回键移除二级悬浮窗if (keyCode == KeyEvent.KEYCODE_BACK&& event.getAction() == KeyEvent.ACTION_DOWN) {floatWindowManager.removeBigWindow();return true;}return super.onKeyDown(keyCode, event);}}

项目下载地址:https://github.com/ZhaoKaiQiang/FloatWindow

【Anroid界面实现】通用的桌面悬浮窗口的实现相关推荐

  1. 通用的桌面悬浮窗口的实现

    现在很多安全类的软件,比如360手机助手,百度手机助手等等,都有一个悬浮窗,可以飘浮在桌面上,方便用户使用一些常用的操作.今天这篇文章,就是介绍如何实现桌面悬浮窗效果的. 首先,看一下效果图. 悬浮窗 ...

  2. Android桌面悬浮窗口举例

    概述 Android项目开发时,有时候需要开发一些悬浮在桌面上的视图.比如桌面小精灵,各种音乐播放器的悬浮播放控制栏等等.本文就借助一个小的demo,用代码的方式大概进行介绍. 原理 开发桌面悬浮窗口 ...

  3. 桌面悬浮窗口(可拖动)

    一.开发前原理简述 桌面悬浮窗口,(如360的清理加速等悬浮按钮),调用WindowManager,并设置WindowManager.LayoutParams的相关属性,通过WindowManager ...

  4. android 添加随意拖动的桌面悬浮窗口,android 添加随意拖动的桌面悬浮窗口

    用过新版本android 360手机助手都人都对 360中只在桌面显示一个小小悬浮窗口羡慕不已吧? 其实实现这种功能,主要有两步: 1.判断当前显示的是为桌面.这个内容我在前面的帖子里面已经有过介绍, ...

  5. android多个悬浮窗口,android 添加随意拖动的桌面悬浮窗口

    用过新版本android 360手机助手都人都对 360中只在桌面显示一个小小悬浮窗口羡慕不已吧? 其实实现这种功能,主要有两步: 1.判断当前显示的是为桌面.这个内容我在前面的帖子里面已经有过介绍, ...

  6. android悬浮窗口的实现

    当我们在手机上使用360安全卫士时,手机屏幕上时刻都会出现一个小浮动窗口,点击该浮动窗口可跳转到安全卫士的操作界面,而且该浮动窗口不受其他activity的覆盖影响仍然可见(多米音乐也有相关的和主界面 ...

  7. android自动悬浮窗代码,Android_Android实现桌面悬浮窗、蒙板效果实例代码,现在很多安全类的软件,比如3 - phpStudy...

    Android实现桌面悬浮窗.蒙板效果实例代码 现在很多安全类的软件,比如360手机助手,百度手机助手等等,都有一个悬浮窗,可以飘浮在桌面上,方便用户使用一些常用的操作. 今天这篇文章,就是介绍如何实 ...

  8. android 实现浮动窗口,Android_圣诞节,写个程序练练手————Android 全界面悬浮按钮实现,开始我以为悬浮窗口,可以用A - phpStudy...

    圣诞节,写个程序练练手----Android 全界面悬浮按钮实现 开始我以为悬浮窗口,可以用Android中得PopupWindow 来实现,虽然也实现了,但局限性非常大.比如PopupWindow必 ...

  9. android视频聊天桌面小窗口怎么实现,android视频通话悬浮窗的适配

    前序 按项目交互要求,需要把视频通话界面,缩小至悬浮窗显示,基本实现思路这个比较好想,就是启用一个service,在里面用WindowManager去addView来展示悬浮窗画面.基本效果是有了,但 ...

最新文章

  1. 仿百度文库方案[openoffice.org 3+swftools+flexpaper](三) 之 使用JODConverter将office文档转换为pdf...
  2. linux shell let命令,shell编程中的let与(())
  3. PagingAndSortingRepository接口与 JpaRepository接口
  4. Spark记录-Scala语法基础
  5. 聊一聊ws2_32.dll和wsock32.dll
  6. 用户管理系统_【20201204】做个用户管理系统(18)——注册功能的实现(三)...
  7. 前端常用60余种工具方法(上)
  8. ESP32串口API
  9. OpenCV imread读取图片,imshow展示图片,出现cv:Exception at memory location异常
  10. AsnycTask的内部的实现机制
  11. centos7.x 网卡自动关闭,总是需要手动去启动情况解决方法
  12. vue中使用使用阿里云的iconfont
  13. 2019APMCM亚太数学建模题目
  14. 用户需求管理 - KANO模型
  15. x86服务器MCE(Machine Check Exception)问题
  16. 为 什 么 说 Synchronized 是 一 个 悲 观 锁 ?
  17. android 应用程序内存上限,Android APP的最大可用内存
  18. python整段注释_python段注释
  19. 柠萌影业三闯IPO,“爆款制造机“更像盲盒?
  20. 给自己一个整洁的办公环境,我是指你的电脑系统

热门文章

  1. linux映射关系文件,Linux磁盘映射DM
  2. java版溺尸掉三叉戟吗_《我的世界》一句话评价老MC的富有?萌新:他有54根三叉戟!...
  3. 硬件环境对系统性能的影响
  4. postgresql-json
  5. 怎么自己做markdown目录?
  6. 华云大咖说 | 安超信创桌面云金融行业解决方案
  7. Ubuntu下tomcat安装
  8. 这个发热鞋垫厉害了,有它冬天再也不怕脚冷
  9. 商业银行、信托、券商等金融机构与融资租赁的合作模式解析
  10. Uni.me免费域名申请注册和使用教程