当前位置: 首页 > 编程日记 > 正文

Android 悬浮窗口

Android 悬浮窗口

一.创建悬浮窗口步骤
    1.实现一个ViewGroup类,作为悬浮窗口的界面类,以便在里面重写onInterceptTouchEvent和onTouchEvent方法,实现移动界面的目的.
      在本例中实现了一个FloatLayer类,可以作为通用的类,使用时需要传入WindowManager对象以实现移动窗口.

// FloatLayer ~
package com.example.hellofloatingwnd;import static com.ahai.util.DebugMessage.d;
import android.content.Context;
import android.graphics.PixelFormat;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.widget.RelativeLayout;public class FloatLayer extends RelativeLayout {// flags: 须设置成 FLAG_NOT_FOCUSABLE, 否则悬浮窗口下面的窗口不能取得焦点, 无法响应触摸事件// type: 值低的窗口在值高的下层,相同的 type值,后创建的窗口显示在先创建的窗口上面.// 对应的type需要相应的权限,否则会报异常 BadTokenException.// 对于权限 android.permission.SYSTEM_ALERT_WINDOW 可使用以下几个值:// TYPE_PHONE, TYPE_SYSTEM_ALERT, TYPE_TOAST, TYPE_SYSTEM_OVERLAY// 其中 TYPE_TOAST, TYPE_SYSTEM_OVERLAY 不能响应触摸事件public static class FloatLayoutParams extends WindowManager.LayoutParams {public FloatLayoutParams() {super(TYPE_PHONE, FLAG_NOT_FOCUSABLE, PixelFormat.RGBA_8888);gravity = Gravity.LEFT | Gravity.TOP;}public FloatLayoutParams(int type) {super(type, FLAG_NOT_FOCUSABLE, PixelFormat.RGBA_8888);gravity = Gravity.LEFT | Gravity.TOP;}public FloatLayoutParams(int xpos, int ypos) {super(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, xpos,ypos, TYPE_PHONE, FLAG_NOT_FOCUSABLE, PixelFormat.RGBA_8888);gravity = Gravity.LEFT | Gravity.TOP;}public FloatLayoutParams(int xpos, int ypos, int type) {super(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, xpos,ypos, type, FLAG_NOT_FOCUSABLE, PixelFormat.RGBA_8888);gravity = Gravity.LEFT | Gravity.TOP;}}private WindowManager mWindowManager;private int mStatusBarHeight;private int mMoveX;private int mMoveY;public FloatLayer(Context context, AttributeSet attrs, int defStyleAttr,int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);mStatusBarHeight = getStatusBarHeight();}public FloatLayer(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mStatusBarHeight = getStatusBarHeight();}public FloatLayer(Context context, AttributeSet attrs) {super(context, attrs);mStatusBarHeight = getStatusBarHeight();}public FloatLayer(Context context) {super(context);mStatusBarHeight = getStatusBarHeight();}public void setWindowManager(WindowManager windowManager) {mWindowManager = windowManager;}/** 取得系统状态栏的高度 */private int getStatusBarHeight() {int statusBarHeight = 0;int resourceId = getResources().getIdentifier("status_bar_height","dimen", "android");if (resourceId > 0) {statusBarHeight = getResources().getDimensionPixelSize(resourceId);}// d("statusBarHeight=" + statusBarHeight);return statusBarHeight;}@Overridepublic boolean onInterceptTouchEvent(MotionEvent event) {final int action = event.getAction();if (action == MotionEvent.ACTION_MOVE) {if (handleMoveEvent(event))return true;} else if (action == MotionEvent.ACTION_DOWN) {mMoveX = (int) event.getRawX();mMoveY = (int) event.getRawY();}return super.onInterceptTouchEvent(event);}@Overridepublic boolean onTouchEvent(MotionEvent event) {final int action = event.getAction();if (action == MotionEvent.ACTION_MOVE) {if (handleMoveEvent(event))return true;} else if (action == MotionEvent.ACTION_DOWN) {mMoveX = (int) event.getRawX();mMoveY = (int) event.getRawY();}return super.onTouchEvent(event);}private boolean handleMoveEvent(MotionEvent event) {try {if (mWindowManager != null) {// 通过以下消息可知getLayoutParams得到的对象即为 addView 传入的 LayoutParams 对象// d("class:" + getLayoutParams().getClass());final int x = (int) event.getRawX();final int y = (int) event.getRawY();int[] location = new int[2];getLocationOnScreen(location);FloatLayoutParams layoutParams = (FloatLayoutParams) getLayoutParams();layoutParams.x = location[0] + (x - mMoveX);layoutParams.y = location[1] + (y - mMoveY) - mStatusBarHeight;mWindowManager.updateViewLayout(this, layoutParams);mMoveX = x;mMoveY = y;return true;}} catch (Exception e) {d("", e);}return false;}
}


    2.在res/layout中创建一个布局文件,实现界面布局.如float_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<com.example.hellofloatingwnd.FloatLayer xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="wrap_content" ><ImageButtonandroid:id="@+id/mBtnHide"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentTop="true"android:layout_centerHorizontal="true"android:background="@drawable/hide_button_selector" /><TextViewandroid:id="@+id/mTvHello"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/mBtnHide"android:layout_centerHorizontal="true"android:text="@string/hello"android:textColor="#21d" /></com.example.hellofloatingwnd.FloatLayer>


    3.取得WindowManager对象.
        在 Activity 中可以通过以下方法取得, 其中前面的3个方法实际取得的是当前Activity的应用程序窗口对象,在Activity销毁等情况下,
    WindowManager对象也就不存在了,需要将悬浮窗口移除,否则会报错.
        WindowManager windowManager;
        windowManager = getWindow().getWindowManager();
        windowManager = getWindowManager();
        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        windowManager = (WindowManager) getApplication().getSystemService(WINDOW_SERVICE);
        在 Service 中, 以下两个方法均可, 在 onDestory 中将悬浮窗口移除即可.
        windowManager = (WindowManager) getApplication().getSystemService(WINDOW_SERVICE);
        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
   
    4.创建View并显示. 通过以下两个行代码完成:
        mFloatView = (FloatLayer) inflater.inflate(R.layout.float_layout, null);
        windowManager.addView(mFloatView, layoutParams);
        其中layoutParams是实现悬浮窗口的关键,窗口的配置及移动都通过其指定.
        LayoutParams需使用WindowManager.LayoutParams对象或继承自该类重写.
        移动窗口通过下面的代码实现:
            mWindowManager.updateViewLayout(mFloatView, layoutParams);
        layoutParams最重要的两个参数是flags和type:
        flags: 须设置成 FLAG_NOT_FOCUSABLE, 否则悬浮窗口下面的窗口不能取得焦点,不能响应触摸事件.
        type: 值低的窗口在值高的下层,相同的 type值,后创建的窗口显示在先创建的窗口上面.
              对应的type需要相应的权限,否则会报异常 BadTokenException.
        对于权限 android.permission.SYSTEM_ALERT_WINDOW, type可使用以下几个值:
            TYPE_PHONE, TYPE_SYSTEM_ALERT, TYPE_TOAST, TYPE_SYSTEM_OVERLAY
        其中 TYPE_TOAST, TYPE_SYSTEM_OVERLAY 不能响应触摸事件

在Service中实现

package com.example.hellofloatingwnd;import android.app.Application;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.Toast;public class FloatingService extends Service {private FloatLayer mFloatView;private WindowManager mWindowManager;private ImageButton mBtnHide;@Overridepublic void onCreate() {super.onCreate();Application app = getApplication();mWindowManager = (WindowManager) app.getSystemService(WINDOW_SERVICE);LayoutInflater inflater = LayoutInflater.from(app);mFloatView = (FloatLayer) inflater.inflate(R.layout.float_layout, null);mFloatView.setWindowManager(mWindowManager);mBtnHide = (ImageButton) mFloatView.findViewById(R.id.mBtnHide);mBtnHide.setOnClickListener(mClickListener);FloatLayer.FloatLayoutParams layoutParams;layoutParams = new FloatLayer.FloatLayoutParams(10, 100);mWindowManager.addView(mFloatView, layoutParams);}@Overridepublic void onDestroy() {super.onDestroy();if (mFloatView != null) {mWindowManager.removeView(mFloatView);mFloatView = null;}}@Overridepublic IBinder onBind(Intent arg0) {return null;}private OnClickListener mClickListener = new OnClickListener() {@Overridepublic void onClick(View view) {if (view.getId() == R.id.mBtnHide) {Toast.makeText(getApplicationContext(),"on float button clicked.", Toast.LENGTH_SHORT).show();FloatingService.this.stopSelf();}}};
}

在Activity中实现

package com.example.hellofloatingwnd;import android.app.Activity;
import android.app.Application;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;public class MainActivity extends Activity {private Button mBtnStart;private Button mBtnStop;private FloatLayer mFloatView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mBtnStart = (Button) findViewById(R.id.mBtnStart);mBtnStop = (Button) findViewById(R.id.mBtnStop);mBtnStart.setOnClickListener(mClickListener);mBtnStop.setOnClickListener(mClickListener);WindowManager windowManager;// windowManager = getWindow().getWindowManager();windowManager = getWindowManager();// windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);// windowManager = (WindowManager)// getApplication().getSystemService(WINDOW_SERVICE);
Application application = getApplication();LayoutInflater inflater = LayoutInflater.from(application);mFloatView = (FloatLayer) inflater.inflate(R.layout.float_layout, null);mFloatView.setWindowManager(windowManager);TextView textView = (TextView) mFloatView.findViewById(R.id.mTvHello);textView.setText("This create by activity.");FloatLayer.FloatLayoutParams layoutParams;layoutParams = new FloatLayer.FloatLayoutParams(50, 200);windowManager.addView(mFloatView, layoutParams);}@Overrideprotected void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();if (mFloatView != null) {WindowManager windowManager = getWindow().getWindowManager();windowManager.removeView(mFloatView);mFloatView = null;}}private OnClickListener mClickListener = new OnClickListener() {@Overridepublic void onClick(View view) {if (view.getId() == R.id.mBtnStart) {Intent intent = new Intent(MainActivity.this,FloatingService.class);startService(intent);} else if (view.getId() == R.id.mBtnStop) {Intent intent = new Intent(MainActivity.this,FloatingService.class);stopService(intent);}}};
}


    5.在AndroidManifest.xml文件中添加权限,在layoutParams中配置不同的type类型需要不同的权限.
        <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

转载于:https://www.cnblogs.com/diysoul/p/5169234.html

相关文章:

Java多线程复习:1(进程和线程、并发和并行)

进程和线程 进程 我们电脑中每一个运行着的程序都是一个进程&#xff0c;程序一旦运行就是进程。 进程可以视为程序的一个实例&#xff0c;大部分程序可以同时运行多个实例进程&#xff08;如&#xff1a;浏览器、记事本、画图等&#xff09;&#xff0c;也有的程序只能启动一…

新年新技术:MongoDB 3.0

前一篇介绍了HTTP/2&#xff0c;这一篇简单介绍下3月3号发布的MongoDB 3.0。 What’s new in MongoDB 3.0? 新的存储引擎WiredTiger MongoDB 3.0的存储引擎是插件式的&#xff0c;默认为新增的WiredTiger。WiredTiger相比原来的MMAPv1引擎的优点&#xff1a; 文档级别的锁这个…

2021全国高校计算机能力挑战赛(初赛)C语言试题四

肯德基有一种奇怪的优惠券&#xff0c;卷上的金额都是整数。活动要求找到3张优惠券总额恰好是商品本身。 #include<stdio.h> int main() {int p,n,f0,temp0;scanf("%d %d",&p,&n);int a[10000];for (int i 0; i < n; i) {scanf("%d",&am…

Substance Painter实时角色制作视频教程

持续时间2h 包含项目文件 1920X1080 MP4 语言&#xff1a;英语中文字幕&#xff08;机译&#xff09; 大小&#xff1a;5.63G Substance Painter实时角色制作视频教程 信息: 本教程旨在为一个实时角色解决实体画师中纹理化现实人类皮肤的奥秘&#xff01; 在这个2小时的全叙述…

Java多线程复习:2(线程的创建和使用)

线程的创建和使用 方法一&#xff0c;直接使用Thread import lombok.extern.slf4j.Slf4j; /*** author&#xff1a;小关同学爱吃汉堡* date: 2021/4/1 23:27*/ Slf4j(topic "c.Test1") public class Test1 {public static void main(String[] args) {//以匿名内部…

Apache htaccess 重写假设文件存在!

假设文件 data/cache/index.html 存在。那么才重写。否则使用默认的MVC 重写。by default.fufoxmail.com RewriteEngine on RewriteCond %{DOCUMENT_ROOT}/data/cache/list_1.html -f RewriteRule ^game\/?$ data/cache/list_1.html [L] RewriteCond %{REQUEST_FILENAME} !-f …

Xamarin只言片语系列

把自己写xamarin中的一些知识点记录下来&#xff0c;分享给大家&#xff0c;欢迎指正。Xamarin只言片语1——Xamarin下的弹框Xamarin只言片语2——Xamarin下的web api操作更新中……

2021全国高校计算机能力挑战赛(初赛)C语言

3-2空间中有N个顶点&#xff08;顶点坐标用x,y,z表示&#xff09;&#xff0c;从中找出能构成三角形图形的最大面积&#xff0c;输出该面积。 #include<stdio.h> #include<math.h> int main() {int n,m0;double a,b,c,p,res[1000],s[1000][3];scanf("%d"…

藤摇椅游戏道具制作全流程讲解视频教程

藤摇杆|一个完整的游戏资产工作流程 MP4 |视频:h264&#xff0c;1280720 |音频:AAC&#xff0c;44.1 KHz&#xff0c;2 Ch 含工程素材 语言&#xff1a;英语中文字幕&#xff08;根据原英文字幕机译更准确&#xff09;原英文字幕 |时长:25节课(9h 57m) |大小:8.35 GB 通过模拟游…

SpringBoot复习:2(@Configuration注解)

Configuration注解 Configuration注解的作用是代替以前我们使用的xml配置文件&#xff0c;被注解的类就是配置类&#xff0c;其内部包含有一个或多个被Bean注解的方法&#xff0c;这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类…

C/C++中constkeyword

今天在做一个趋势笔试题的时候。才让我有了系统把constkeyword好好总结一下的冲动&#xff0c;由于这个关键词大大小小好多地方都出现过&#xff0c;出现频率很高&#xff0c;而每次仅仅是简短的把答案看了一下&#xff0c;没有真正将其整个使用方法弄透&#xff0c;立即要找工…

hdu5185 dp:和为n且满足后一项是前一项或者+1的数列个数

其实是很裸的dp&#xff0c;竟然放在第四题 dp[i][j]表示当前放j和为i的方案数 dp[0][0]1; dp[i][j]dp[i-j][j-1]dp[i-j][j]; 1 #include<stdio.h>2 #include<string.h>3 #include<math.h>4 #include<algorithm>5 using namespace std;6 int dp[50005]…

2021全国高校计算机能力挑战赛(决赛)Java

计算奖品价格标准差 import java.util.*;public class Main {public static void main(String[] args) {Scanner in new Scanner(System.in);int n in.nextInt();float sum0;int total_n0;int[] numnew int[10];float[] prinew float[10];for (int i 0; i <n ; i) {num[i…

Python训练营2021:构建8个真实世界的Python项目

时长:19h 27m |视频:. MP4&#xff0c;1280720 30 fps |音频:AAC&#xff0c;44.1 kHz&#xff0c;2ch |大小:9.54 GB 语言&#xff1a;英语中英文字幕 机译 从Python Web开发的初学者到专家:学习使用SQLite、Tkinter开发真实的Django应用程序 你会学到: 了解如何使用像Djang…

SpringBoot复习:3(@Conditional)

Conditional 条件装配&#xff1a;满足Conditional指定的条件时&#xff0c;则进行组件注入 Conditional注解下面包含许多注解&#xff0c;如下图&#xff1a; 这些注解各有各的功能&#xff0c;这里挑几个来记录一下。 ConditionalOnBean注解 个人理解&#xff1a;当该注…

HDU——1106排序(istringstream的使用、STLvector练习)

排序 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 46057 Accepted Submission(s): 13221 Problem Description输入一行数字&#xff0c;如果我们把这行数字中的‘5’都看成空格&#xff0c;那么就得到一行用…

AssertValid函数学习

转自http://tsitao.blog.163.com/blog/static/29795822006914105840496/ VC的调试中&#xff0c;AssertValid和Dump函数的应用 CObject::AssertValid 成员函数提供对对象内部状态的运行时检查。虽然从 CObject 派生类时不须要重写 AssertValid&#xff0c;但能够通过重写使您的…

数值分析第一次作业-牛顿迭代法求解二元非线性方程组

1、问题 求解如下方程组: 2、算法 3、代码实现 # *coding:utf-8 * import math delta 5e-6 ;eps 1e-6 x0 1;y0 1 er 1;k 0 def z(x,y):return math.sin(x*y)*math.exp(-0.1*(x**2y**2x*y2*x)) def f(x,y):return y*math.cos(x*y)-0.1*(2*xy2)*math.sin(x*y) def g(x,y…

Maya制作风格化的女性跑步动画学习教程

时长:2h 11m |视频:. MP4 1280720&#xff0c;30 fps(r) |音频:AAC&#xff0c;48000 Hz&#xff0c;2ch |大小解压后:1.56 GB 含参考素材 流派:电子学习|语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09; 通过在Autodesk Maya中提供专业外观…

安卓手机抓包实战

最近因为要做一个项目&#xff0c;所以需要对手机给家电发送的请求进行抓取&#xff0c;于是上网找了许多教程&#xff0c;但都不怎么理想&#xff0c;最终经过两个晚上的艰辛奋斗&#xff0c;终于让我找到了方法抓取到了手机发送的请求&#xff0c;写一篇博客记录一下这个过程…

导入drupal中文语言包

http://lugir.com/book/export/html/399转载于:https://www.cnblogs.com/qinqiu/p/5174062.html

JavaScript引擎大战:Google提出StrongMode和SoundScript议案,增强V8性能

JavaScript 引擎性能大战硝烟迷漫&#xff0c;Chrome V8 引擎团队宣布了一个新的计划&#xff0c;在这个计划里他们介绍了两个新的虚拟机概念&#xff1a;强化模式&#xff08;StrongMode&#xff09;和强化脚本&#xff08;StrongScript&#xff09;。\\强化模式是 ES5 严格模…

数值分析第三次作业-常微分方程的数值解法

一、欧拉方法 二、修正的欧拉方法 2.1.梯形公式 将问题&#xff08;*&#xff09;的解表成积分形式&#xff0c;用数值积分方法离散化。例如&#xff0c;对微分方程

SYNCHRO 4D可视化调度学习教程 SYNCHRO 4D: Visual Scheduling

MP4 |视频:h264&#xff0c;1280720 |音频:AAC&#xff0c;44.1 KHz&#xff0c;2 Ch 技能水平:初级|流派:电子学习|语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09; |时长:3h 14m |大小:1.36 GB 含课程练习文件 课程获取&#xff1a;SYNCHR…

SpringBoot复习:4(@ImportResource)

ImportResource注解 作用&#xff1a;这个注解可以用来导入Spring的配置文件&#xff08;bean.xml文件&#xff09;。 适用场景&#xff1a; 可以试想一下这样一个场景&#xff0c;老板要你将原来的Spring项目改成SpringBoot项目&#xff0c;原项目底下有一大堆的bean.xml文件…

YTU 2723: 默认参数--求圆的面积

2723: 默认参数--求圆的面积 时间限制: 1 Sec 内存限制: 128 MB提交: 206 解决: 150题目描述 根据半径r求圆的面积, 如果不指定小数位数,输出结果默认保留两位小数。 部分代码已给定如下&#xff0c;只需要提交缺失的代码。 #include <iostream> #include <iomanip&…

jmeter内存溢出处理方式记录

方法一&#xff1a; 使用jmeter进行压力测试时 遇到一段时间后报内存溢出outfmenmory错误&#xff0c;导致jmeter卡死了&#xff0c;先尝试在jmeter.bat中增加了JVM_ARGS"- Xmx2048m -Xms2048m -Xmn256m -XX:PermSize128m -Xss256k"&#xff0c;但结果运行时间增加了…

支持向量机学习笔记1

1、支持向量机是一种二类分类模型。它的模型基本定义是特征空间上间隔最大的线性分类器。 2、核函数的引入&#xff0c;使得支持向量机可以进行非线性分类。 3、支持向量机也可以完成多类别分类&#xff08;将在后面介绍&#xff09; 4、支持向量机的学习策略为间隔最大化&a…

C4D入门学习教程

MP4 |视频:AVC&#xff0c;1280720 30 fps |音频:AAC&#xff0c;48 KHz&#xff0c;2 Ch |时长:2h 12m 语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09;|大小解压后:560M C4D是一个有抱负的运动图形艺术家和设计师的重要工具。借助C4D&#…

SpringBoot复习:5(配置绑定)

配置绑定 在SpringBoot中我们如何读取到properties文件中的内容&#xff0c;并且把它封装到JavaBean中&#xff0c;以供随时使用呢&#xff1f; 我们可以使用以下两种方式来实现。 方法一&#xff1a;ConfigurationPropertiesComponent 第一种方法&#xff0c;我们可以使用C…