Android常用控件之悬浮窗

2023-11-05

  悬浮窗可以显示在所有应用程序之上,不管在PC机还是Android设备上都有这个,最常见的是360的“加速球”


来看下在Android设备上的效果


程序的目录结构如下图


创建Activity后启动Service就关闭

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.example.floatwnd;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.Intent;  
  5. import android.os.Bundle;  
  6.   
  7. import com.example.floatwnd.service.FloatService;  
  8.   
  9. public class MainActivity extends Activity {  
  10.   
  11.     @Override  
  12.     protected void onCreate(Bundle savedInstanceState) {  
  13.         super.onCreate(savedInstanceState);  
  14.         Intent intent = new Intent(MainActivity.this, FloatService.class);  
  15.         startService(intent);  
  16.         finish();  
  17.     }  
  18. }  

悬浮窗的主要工作放在Service上处理

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.example.floatwnd.service;  
  2.   
  3. import android.app.Service;  
  4. import android.content.Intent;  
  5. import android.graphics.PixelFormat;  
  6. import android.graphics.Rect;  
  7. import android.os.Handler;  
  8. import android.os.IBinder;  
  9. import android.os.Looper;  
  10. import android.os.Message;  
  11. import android.view.Gravity;  
  12. import android.view.LayoutInflater;  
  13. import android.view.MotionEvent;  
  14. import android.view.View;  
  15. import android.view.View.OnClickListener;  
  16. import android.view.View.OnTouchListener;  
  17. import android.view.WindowManager;  
  18. import android.view.WindowManager.LayoutParams;  
  19. import android.widget.Button;  
  20. import android.widget.TextView;  
  21. import android.widget.Toast;  
  22.   
  23. import com.example.floatwnd.R;  
  24. import com.example.floatwnd.utils.SysInfoUtils;  
  25.   
  26. /** 
  27.  * 悬浮窗Service 该服务会在后台一直运行一个悬浮的透明的窗体 
  28.  *  
  29.  * @author Administrator 
  30.  *  
  31.  */  
  32. public class FloatService extends Service {  
  33.   
  34.     private static final int UPDATE_PIC = 0x100;  
  35.     private int statusBarHeight;// 状态栏高度  
  36.     private View view;// 透明窗体  
  37.     private TextView text = null;  
  38.     private Button hideBtn = null;  
  39.     private Button updateBtn = null;  
  40.     private HandlerUI handler = null;  
  41.     private Thread updateThread = null;  
  42.     private boolean viewAdded = false;// 透明窗体是否已经显示  
  43.     private boolean viewHide = false// 窗口隐藏  
  44.     private WindowManager windowManager;  
  45.     private WindowManager.LayoutParams layoutParams;  
  46.   
  47.     @Override  
  48.     public IBinder onBind(Intent arg0) {  
  49.         // TODO Auto-generated method stub  
  50.         return null;  
  51.     }  
  52.   
  53.     @Override  
  54.     public void onCreate() {  
  55.         // TODO Auto-generated method stub  
  56.         super.onCreate();  
  57.         createFloatView();  
  58.     }  
  59.   
  60.     @Override  
  61.     public void onStart(Intent intent, int startId) {  
  62.         // TODO Auto-generated method stub  
  63.         super.onStart(intent, startId);  
  64.         System.out.println("------------------onStart");  
  65.         viewHide = false;  
  66.         refresh();  
  67.     }  
  68.   
  69.     @Override  
  70.     public void onDestroy() {  
  71.         // TODO Auto-generated method stub  
  72.         super.onDestroy();  
  73.         removeView();  
  74.     }  
  75.   
  76.     /** 
  77.      * 关闭悬浮窗 
  78.      */  
  79.     public void removeView() {  
  80.         if (viewAdded) {  
  81.             windowManager.removeView(view);  
  82.             viewAdded = false;  
  83.         }  
  84.     }  
  85.   
  86.     private void createFloatView() {  
  87.         handler = new HandlerUI();  
  88.         UpdateUI update = new UpdateUI();  
  89.         updateThread = new Thread(update);  
  90.         updateThread.start(); // 开户线程  
  91.   
  92.         view = LayoutInflater.from(this).inflate(R.layout.main, null);  
  93.         text = (TextView) view.findViewById(R.id.usage);  
  94.         hideBtn = (Button) view.findViewById(R.id.hideBtn);  
  95.         updateBtn = (Button) view.findViewById(R.id.updateBtn);  
  96.         windowManager = (WindowManager) this.getSystemService(WINDOW_SERVICE);  
  97.         /* 
  98.          * LayoutParams.TYPE_SYSTEM_ERROR:保证该悬浮窗所有View的最上层 
  99.          * LayoutParams.FLAG_NOT_FOCUSABLE:该浮动窗不会获得焦点,但可以获得拖动 
  100.          * PixelFormat.TRANSPARENT:悬浮窗透明 
  101.          */  
  102.         layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,  
  103.                 LayoutParams.WRAP_CONTENT, LayoutParams.TYPE_SYSTEM_ERROR,  
  104.                 LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);  
  105.         // layoutParams.gravity = Gravity.RIGHT|Gravity.BOTTOM; //悬浮窗开始在右下角显示  
  106.         layoutParams.gravity = Gravity.LEFT | Gravity.TOP;  
  107.   
  108.         /** 
  109.          * 监听窗体移动事件 
  110.          */  
  111.         view.setOnTouchListener(new OnTouchListener() {  
  112.             float[] temp = new float[] { 0f, 0f };  
  113.   
  114.             public boolean onTouch(View v, MotionEvent event) {  
  115.                 layoutParams.gravity = Gravity.LEFT | Gravity.TOP;  
  116.                 int eventaction = event.getAction();  
  117.                 switch (eventaction) {  
  118.                 case MotionEvent.ACTION_DOWN: // 按下事件,记录按下时手指在悬浮窗的XY坐标值  
  119.                     temp[0] = event.getX();  
  120.                     temp[1] = event.getY();  
  121.                     break;  
  122.   
  123.                 case MotionEvent.ACTION_MOVE:  
  124.                     refreshView((int) (event.getRawX() - temp[0]),  
  125.                             (int) (event.getRawY() - temp[1]));  
  126.                     break;  
  127.   
  128.                 }  
  129.                 return true;  
  130.             }  
  131.         });  
  132.   
  133.         hideBtn.setOnClickListener(new OnClickListener() {  
  134.   
  135.             @Override  
  136.             public void onClick(View v) {  
  137.                 // TODO Auto-generated method stub  
  138.                 viewHide = true;  
  139.                 removeView();  
  140.                 System.out.println("----------hideBtn");  
  141.             }  
  142.         });  
  143.   
  144.         updateBtn.setOnClickListener(new OnClickListener() {  
  145.   
  146.             @Override  
  147.             public void onClick(View v) {  
  148.                 // TODO Auto-generated method stub  
  149.                 Toast.makeText(getApplicationContext(), "you click UpdateBtn",  
  150.                         Toast.LENGTH_SHORT).show();  
  151.                 System.out.println("mom "  
  152.                         + SysInfoUtils  
  153.                                 .getUsedPercentValue(getApplicationContext()));  
  154.             }  
  155.         });  
  156.     }  
  157.   
  158.     /** 
  159.      * 刷新悬浮窗 
  160.      *  
  161.      * @param x 
  162.      *            拖动后的X轴坐标 
  163.      * @param y 
  164.      *            拖动后的Y轴坐标 
  165.      */  
  166.     private void refreshView(int x, int y) {  
  167.         // 状态栏高度不能立即取,不然得到的值是0  
  168.         if (statusBarHeight == 0) {  
  169.             View rootView = view.getRootView();  
  170.             Rect r = new Rect();  
  171.             rootView.getWindowVisibleDisplayFrame(r);  
  172.             statusBarHeight = r.top;  
  173.         }  
  174.   
  175.         layoutParams.x = x;  
  176.         // y轴减去状态栏的高度,因为状态栏不是用户可以绘制的区域,不然拖动的时候会有跳动  
  177.         layoutParams.y = y - statusBarHeight;// STATUS_HEIGHT;  
  178.         refresh();  
  179.     }  
  180.   
  181.     /** 
  182.      * 添加悬浮窗或者更新悬浮窗 如果悬浮窗还没添加则添加 如果已经添加则更新其位置 
  183.      */  
  184.     private void refresh() {  
  185.         // 如果已经添加了就只更新view  
  186.         if (viewAdded) {  
  187.             windowManager.updateViewLayout(view, layoutParams);  
  188.         } else {  
  189.             windowManager.addView(view, layoutParams);  
  190.             viewAdded = true;  
  191.         }  
  192.     }  
  193.   
  194.     /** 
  195.      * 接受消息和处理消息 
  196.      *  
  197.      * @author Administrator 
  198.      *  
  199.      */  
  200.     class HandlerUI extends Handler {  
  201.         public HandlerUI() {  
  202.   
  203.         }  
  204.   
  205.         public HandlerUI(Looper looper) {  
  206.             super(looper);  
  207.         }  
  208.   
  209.         /** 
  210.          * 接收消息 
  211.          */  
  212.         @Override  
  213.         public void handleMessage(Message msg) {  
  214.             // TODO Auto-generated method stub  
  215.             // 根据收到的消息分别处理  
  216.             if (msg.what == UPDATE_PIC) {  
  217.                 text.setText(SysInfoUtils  
  218.                         .getUsedPercentValue(getApplicationContext())  
  219.                         + "  t = "  
  220.                         + SysInfoUtils.getTotalMemory(getApplicationContext())  
  221.                         + "  a = "  
  222.                         + SysInfoUtils  
  223.                                 .getAvailableMemoryString(getApplicationContext()));  
  224.                 if (!viewHide)  
  225.                     refresh();  
  226.             } else {  
  227.                 super.handleMessage(msg);  
  228.             }  
  229.   
  230.         }  
  231.   
  232.     }  
  233.   
  234.     /** 
  235.      * 更新悬浮窗的信息 
  236.      *  
  237.      * @author Administrator 
  238.      *  
  239.      */  
  240.     class UpdateUI implements Runnable {  
  241.   
  242.         @Override  
  243.         public void run() {  
  244.             // TODO Auto-generated method stub  
  245.             // 如果没有中断就一直运行  
  246.             while (!Thread.currentThread().isInterrupted()) {  
  247.                 Message msg = handler.obtainMessage();  
  248.                 msg.what = UPDATE_PIC; // 设置消息标识  
  249.                 handler.sendMessage(msg);  
  250.                 // 休眠1s  
  251.                 try {  
  252.                     Thread.sleep(1000);  
  253.                 } catch (InterruptedException e) {  
  254.                     // TODO Auto-generated catch block  
  255.                     e.printStackTrace();  
  256.                 }  
  257.             }  
  258.         }  
  259.     }  
  260. }  

为悬浮窗创建布局文件

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="wrap_content"  
  4.     android:layout_height="wrap_content"  
  5.     android:background="@drawable/background" >  
  6.   
  7.     <!-- android:updatePeriodMillis="10000"> -->  
  8.   
  9.     <TextView  
  10.         android:id="@+id/flowspeed"  
  11.         android:layout_width="wrap_content"  
  12.         android:layout_height="wrap_content"  
  13.         android:layout_alignParentTop="true"  
  14.         android:layout_centerInParent="true"  
  15.         android:text="@string/float_text"  
  16.         android:textColor="#000000" />  
  17.   
  18.     <Button  
  19.         android:id="@+id/hideBtn"  
  20.         android:layout_width="wrap_content"  
  21.         android:layout_height="wrap_content"  
  22.         android:layout_below="@id/flowspeed"  
  23.         android:layout_centerInParent="true"  
  24.         android:layout_marginTop="15dp"  
  25.         android:background="@drawable/hide"  
  26.         android:contentDescription="@string/hide"  
  27.         android:text="@string/hide" />  
  28.   
  29.     <Button  
  30.         android:id="@+id/updateBtn"  
  31.         android:layout_width="wrap_content"  
  32.         android:layout_height="wrap_content"  
  33.         android:layout_below="@id/hideBtn"  
  34.         android:layout_centerInParent="true"  
  35.         android:background="@drawable/update"  
  36.         android:contentDescription="@string/update"  
  37.         android:paddingTop="15dp"  
  38.         android:text="@string/update" />  
  39.   
  40.     <TextView  
  41.         android:id="@+id/usage"  
  42.         android:layout_width="wrap_content"  
  43.         android:layout_height="wrap_content"  
  44.         android:layout_below="@id/updateBtn"  
  45.         android:layout_centerInParent="true"  
  46.         android:paddingTop="15dp"  
  47.         android:text="@string/float_text"  
  48.         android:textColor="#000000" />  
  49.   
  50. </RelativeLayout>  

悬浮窗上的控件就跟Activity上一样使用

悬浮窗上显示的一些内存信息,通过工具类:SysInfoUtils.java来读取


完整的Demo可以从以下地址下载:

点击打开链接







本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android常用控件之悬浮窗 的相关文章

随机推荐

  • ecshop漏洞修复整理

    1 ECShop存在一个盲注漏洞 问题存在于 api client api php文件中 提交特制的恶意POST请求可进行SQL注入攻击 可获得敏感信息或操作数据库 路径 api client includes lib api php 参照
  • ABAP常用系统变量 sy-

    直接在syst结构里面可以找到 abap系统变量 SY SUBRC 系统执行某指令后 表示执行成功与否的变量 0表示成功 SY DBLNT 被处理过的记录的笔数 SY UNAME 当前使用者登入SAP的USERNAME SY DATUM 当
  • Redis 基础总结

    1 NoSQL概述 1 1 数据库分类 目前数据库分 关系型数据库与非关系型数据库 常用的关系型数据库 Oracle MySQL SqlServer DB2 常用的非关系数据库 Redis MongoDB ElasticSearch Hba
  • python实现消消乐小游戏

    import random import time 游戏面板大小 BOARD SIZE 10 图案种类数 PATTERN COUNT 4 相邻图案个数 ADJACENT COUNT 3 游戏时间限制 秒 GAME TIME 60 得分表 S
  • 记录Vue3+ts+ElementPlus 多层级表头动态表格创建

    因为工作需要 需要一个如上图 不确定层级的 多层级表头的表格 搜索网上的方法 多为vue2 element ui的方法 查到的vue3 Element plus的方法仅为建立两层级表格 经借鉴大神方法并修改成功 供自己留存 大神略过 一 创
  • Unity3D开发之Matrix4x4矩阵变换

    在Unity开发中时常会用到Matrix4x4矩阵来变换场景中对象的位置 旋转和缩放 但是很多人都不太理解这儿Matrix4x4变换矩阵 通过DX中的变换矩阵我来讲一讲在unity中这个变换矩阵是怎么变换的 在三维图形程序中 我们可以用几何
  • 稀疏数组(最详解)

    概念 当一个数组中大部分元素为0 或者为同一值的数组时 可以使用稀疏数组来保存该数组 稀疏数组的处理方式是 记录数组一共有几行几列 有多少个不同值 把具有不同值的元素和行列及值记录在一个小规模的数组中 从而缩小程序的规模 如下图 左边是原始
  • 左手坐标系和右手坐标系

    转自 https blog csdn net xiaoluoshan article details 53384103 基本的数学知识 左手坐标系和右手坐标系 这些对于搞图像开发或者游戏开发的朋友来说 应该是很基础的东西 不过对于大部分人来
  • VB mschart控件的使用

    一 先看个小例子 Private Sub Form Load Dim MyData 20 1 As Double x轴坐标值 Y轴坐标值 MyData 0 0 0 MyData 0 1 180 本句代表了 第一点数据的X轴坐标为0 Y轴坐标
  • UCOS2的文件目录

    想着闲着也是闲着 把之前学习ucos2源码的笔记整理一下 复盘一次 总结内容将其写为博客作为学习的输出 一 为什么要学RTOS或者IOTOS 我在大一时 开始进入实验室接触单片机 摸爬滚打的参加了几次比赛 也因此入了嵌入式的坑 大三时开始思
  • 一位年薪40W的测试被开除,回怼的一番话,令人沉思

    一位年薪40W测试工程师被开除回怼道 反正我有技术 在哪不一样 一技傍身 万事不愁 当我们掌握了一技之长后 在职场上说话就硬气了许多 不用担心被炒 反过来还可以炒了老板 这一点在码农界特别明显 许多测试人在辞职时 都有一种心态 烂公司 烂领
  • 学习第一天const

    constant 指针与const const char a 指向const对象的指针或者说指向常量的指针 char const a 同上 char const a 指向类型对象的const指针 或者说常指针 const指针 const c
  • ORACLE集群管理-19c RAC ipv6+IPV4双栈配置实战

    关于IPV6支持问题 单实例环境要支持IPV6 数据库版本至少11 2 0 4版本 其实从linux7开始系统默认开启ipv6 怎么确认ipv6是否开启呢 下面介绍两种常见的方法 1 通过查看网卡属性确定 ifconfig a 命令输出有
  • Vue计算两个datetime共多少天

    假如starttime和endtime都是YYYY MM DD HH mm ss类型 将选择器的默认时间格式 object 转换成时间戳 开始时间减去结束时间 时间戳的形式进行运算 s然后转换成天数 通过toFixed函数保留两位小数 th
  • c语言中swap的意思,C语言中swap的作用和用法?

    慕村225694 swap函数一般是一个程序员自定义函数 通常是实现两个变量数值的交换 比如123int a 2 int b 3 swap a b 一般用到变量数值交换 交换后a 3 b 2 实现的方法多种多样 比如下面几种写法 1 通过使
  • Python——类的方法重写、property、运算符重载

    1 super 函数 主要是用来调用父类的方法 在子类中调用父类的方法时进行使用 2 私有方法 私有属性 1 定义方法 在类的内部 使用def关键字可以为类定义一个方法 与一般函数定义不同 类方法必须包含参数self 且为第一个参数 2 私
  • ubuntu搭建vpn步骤

    1 搭建环境 系统 Ubuntu 18 04 4 LTS Bionic Beaver 位置 轻量应用云服务器 2 安装软件 Sudo apt get update Sudo apt get install pptpd Sudo apt ge
  • 【ESP8266】关于调试fatal exception/自动重启的一些经验分享

    本人小白一枚 最近在捣鼓ESP8266的NONOS SDK开发 本来已经写好了一个工程测试基本功能也没什么问题了 但是发现了一个很严重的问题 就是每次一跑上40来分钟的时候 就会宕机重启 自动重启 真是奇了个怪了 本来这也没啥 但出于对稳定
  • 关闭 Ubuntu 中的关机/重启确认的小技巧

    导读 对于 Ubuntu 新手来说 有很多新东西要学 但是网上很多教程不是针对新手的 在这里 我们不走寻常路 不能说全部的教程都是为初学者准备 但至少大部分是 关闭 Ubuntu 中的关机 重启确认 这篇文章也是一篇新手教程 并且展示如何在
  • Android常用控件之悬浮窗

    悬浮窗可以显示在所有应用程序之上 不管在PC机还是Android设备上都有这个 最常见的是360的 加速球 来看下在Android设备上的效果 程序的目录结构如下图 创建Activity后启动Service就关闭 java view pla