Android实现桌面悬浮窗、蒙板效果实例代码

现在很多安全类的软件,比如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 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();

// 设置显示类型为phone

smallWindowParams.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")

@Override

public 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() {

@Override

public 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;

@Override

protected 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() {

@Override

public void click() {

floatWindowManager.createBigWindow(context);

}

});

}

/**

* 移除所有的悬浮窗

*

* @param view

*/

public void remove(View view) {

floatWindowManager.removeAll();

}

@Override

public 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

在上面文章中,我们介绍了如何实现桌面悬浮窗口,在这个效果的实现过程中,最重要的一个类就是WindowManager,今天这篇文章,将对WindowManager的使用进行介绍,并且实现一个使用WindowManager来实现用户打开APP,显示首次使用教学蒙板的效果。

WindowManager类实现了ViewManager接口,ViewManager接口允许我们在Activity上添加或者是移除view,因此WindowManager也允许我们在Activity上进行View的添加和移除操作。

我们可以通过下面的方法获取一个WindowManager对象

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

  1. html页面色块布局代码,Html 实现动态显示颜色块的报表效果(实例代码)

    利用html的颜色块动态展示数据 *{ padding: 0; margin: 0; } .tubiao,.jihua,.shiji,.riqi{ width: 100%; overflow: hid ...

  2. 图书浏览界面html代码,javaScript+turn.js实现图书翻页效果实例代码

    为了实现图书翻页的效果我们在网上可以看到很多教程 在这里推荐turn.js 网上的turn.js 有api 不过是英文的  很多人看起来不方便 .关于代码也是奇形怪状在这里我将详细讲解如何使用turn ...

  3. android 今日头条加载动画,Android 仿今日头条简单的刷新效果实例代码

    点击按钮,先自动进行下拉刷新,也可以手动刷新,刷新完后,最后就多一行数据.有四个选项卡. 前两天导师要求做一个给本科学生预定机房座位的app,出发点来自这里.做着做着遇到很多问题,都解决了.这个效果感 ...

  4. Android 自定义 圆环,Android自定义view实现圆环效果实例代码

    先上效果图,如果大家感觉不错,请参考实现代码. 重要的是如何实现自定义的view效果 (1)创建类,继承view,重写onDraw和onMesure方法 public class CirclePerc ...

  5. android 动态画直线,Android使用自定义view在指定时间内匀速画一条直线的实例代码...

    本文讲述了Android使用自定义view在指定时间内匀速画一条直线的实例代码.分享给大家供大家参考,具体如下: 1.效果图: 2.自定义view实现 public class UniformLine ...

  6. android 动态生成直线,Android使用自定义view在指定时间内匀速画一条直线的实例代码...

    本文讲述了Android使用自定义view在指定时间内匀速画一条直线的实例代码.分享给大家供大家参考,具体如下: 1.效果图: 2.自定义view实现 public class UniformLine ...

  7. android 给图片蒙上蒙层_Android 新手引导蒙层效果实现代码示例

    先上效果图: 这个效果一开始我是想直接让UI给个切图,后来发现这样不行,适配很差,达不到效果.所以就自己动手写代码,其实思路也很简单:在这个布局的父布局上面再手动添加一个view(通常LinearLa ...

  8. android+底部评论框,Android 之BottomsheetDialogFragment仿抖音评论底部弹出对话框效果(实例代码)...

    实现的效果图: 自定义Fragment继承BottomSheetDialogFragment 重写它的三个方法: onCreateDialog() onCreateView() onStart() 他 ...

  9. Android用户首次打开APP的使用教学蒙板效果实现

    转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992 现在很多安全类的软件,比如360手机助手,百度手机助手等等,都有一个悬浮窗,可以飘浮在桌面上,方便用户使用一 ...

最新文章

  1. SQL Server AlwaysOn 集群 关于主Server IP与Listener IP调换的详细测试
  2. 鸿蒙系统速度和ios,鸿蒙系统到底和苹果安卓有什么区别
  3. 硬件设计基础:32种EMC标准电路
  4. higtech软件的使用
  5. 贝叶斯网络:故障诊断方法研究
  6. Python番外篇:segno模块制作WiFi二维码
  7. 单片机c语言中断程序实验报告,单片机实验之外部中断应用实验
  8. 《EfficientDet:Scalable and Efficient Object Detection》论文笔记
  9. 假如杨笠吐槽程序员。。。
  10. uva11134 -Fabled Rooks
  11. python比java好学吗-java为什么比python快?
  12. 临界区、互斥量、事件、信号量四种方式
  13. 现代密码学之数字签名
  14. 图像处理: 设计 自定义透明度 水印
  15. 骨科医疗器械行业产业链及技术水平趋势、竞争格局、主要壁垒构成
  16. 解析自适应滤波回声消除
  17. 汉诺塔实现-PHP版
  18. teraterm软件download的地址
  19. 基于Python的DELMIA二次开发(一):创建零件
  20. 译——冠状病毒:这是全球离岸测试模型的终结吗?

热门文章

  1. 思维探索者:我们需要演绎与归纳
  2. 数据结构——双链表(C语言详述通用双链表)
  3. mov格式怎么转换为mp4
  4. 爬取豆瓣读书Top250,导入sqlist数据库(或excel表格)中
  5. 思维导图ubuntu_Ubuntu系统---安装思维导图XMind
  6. Visual C++ 游戏开发笔记 纹理映射技术(一)
  7. 设计PCB时 Layout软件如何开窗
  8. 2022河北太阳能光伏储能展会
  9. JavaScript实现深拷贝
  10. ME3616 OpenSDK编译