android 悬浮窗口和主界面同时显示,Android 悬浮窗口(及解决6.0以上无法显示问题)...

思路实现

通过WindowManager添加一个View,创建一个系统顶级的窗口,实现悬浮窗口的效果。

本篇思路,来源于郭霖大神的悬浮窗口教程。

大致介绍WindowManager 类

创建的对象:

Context.getSystemService(Context.WINDOW_SERVICE)

常用API:

addView():添加一个View对象

updateViewLayout():更新指定的View对象

removeView():移除一个View对象

使用Kotlin编程,实战开发

1. 编写弹窗中布局文件,item_message.xml:

android:orientation="vertical"

android:id="@+id/suspension_window_layout"

android:layout_width="wrap_content"

android:layout_height="wrap_content">

android:text="系统悬浮弹窗"

android:textColor="@android:color/white"

android:textSize="18sp"

android:padding="10dp"

android:background="@color/colorPrimary"

android:layout_height="wrap_content" />

2. 编写悬浮弹窗的View:

定义一个布局,将对应的item_message.xml绑定上,重写onTouche()悬浮弹窗,实现自动拖动,点击关闭的效果。

class SuspensionWindowLayout(context: Context) : RelativeLayout(context) {

/**

* statusbar系统状态栏的高度

*/

var statusbarHeight = 0

/**

* 窗口管理器

*/

val windowManager: WindowManager

init {

windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager

var childView= View.inflate(context, R.layout.item_message,this)

widget_width = childView.suspension_window_layout.layoutParams.width

widget_height = childView.suspension_window_layout.layoutParams.height

}

/**

* 按下屏幕时手指在x,y轴上的坐标

*/

var down_x = 0.0f

var down_y = down_x

/**

* 移动时候的手指在x,y轴上的坐标

*/

var move_x = down_x

var move_y = down_x

/**

* 按下屏幕时候,控件在x,y轴位置

*/

var widget_x = down_x

var widget_y = down_x

/**

* 重写处理拖动事件

*/

override fun onTouchEvent(event: MotionEvent): Boolean {

when (event.action) {

MotionEvent.ACTION_DOWN -> {

// 手指按下时记录必要数据,纵坐标的值都需要减去状态栏高度

widget_x = event.x

widget_y = event.x

//没有移动,down->up,点击事件

down_x = event.rawX

down_y = event.rawY - getStatusBarHeight()

move_x = event.rawX

move_y = event.rawY - getStatusBarHeight()

}

MotionEvent.ACTION_MOVE -> {

// 手指移动的时候更新小悬浮窗的位置

move_x = event.rawX

move_y = event.rawY - getStatusBarHeight()

updateWidgetPostion()

}

MotionEvent.ACTION_UP -> {//

//坐标没有改变,是点击动作

if (move_x == down_x && move_y == down_y) {

SuspensionWindowManagerUtils.removeSuspensionWindow(context)

}

}

else -> {

}

}

return true

}

/**

* 更新控件位置,在x,y轴的的位置

*/

fun updateWidgetPostion() {

var layoutParams = SuspensionWindowManagerUtils.getWidgetLayoutParams()

layoutParams!!.x = (move_x - widget_x).toInt()

layoutParams!!.y = (move_y - widget_y).toInt()

windowManager.updateViewLayout(this, layoutParams)

}

/**

* 获取系统状态栏,返回状态栏高度的像素值

*/

fun getStatusBarHeight(): Int {

if (statusbarHeight == 0) {

statusbarHeight = resources.getDimensionPixelSize(ViewUtils.getStatusBarHeight())

}

return statusbarHeight

}

companion object {

var widget_width = 0

var widget_height = 0

}

}

一个工具类:

public class ViewUtils {

/**

* 反射获取状态栏高度

*@return

*/

public static int getStatusBarHeight(){

int x=0;

try {

Class> c = Class.forName("com.android.internal.R$dimen");

Object o = c.newInstance();

Field field = c.getField("status_bar_height");

x = (Integer) field.get(o);

} catch (Exception e) {

e.printStackTrace();

}

return x;

}

}

3. 编写WindowManager工具类:

一些列,检查弹窗,开启弹窗,关闭弹窗的操作封装到该类中。

class SuspensionWindowManagerUtils {

companion object {

var windowManager: WindowManager?=null

var layoutParams: WindowManager.LayoutParams?=null

var suspensionWindowWidget: SuspensionWindowLayout? = null

/**

* 创建悬浮窗口

*/

@JvmStatic

fun createSuspensionWindow(context: Context) {

if (suspensionWindowWidget==null){

suspensionWindowWidget= SuspensionWindowLayout(context)

}

getWindowManager(context)!!.addView(suspensionWindowWidget, getWidgetLayoutParams())

}

/**

* 移除悬浮窗口

*/

fun removeSuspensionWindow(context: Context) {

if (suspensionWindowWidget != null) {

getWindowManager(context)!!.removeView(suspensionWindowWidget)

suspensionWindowWidget = null

}

}

/**

* 悬浮窗口是否已经打开

*/

fun windowIsOpen():Boolean{

if (suspensionWindowWidget!=null)

return true

else return false

}

/**

* 获取悬浮窗口的布局参数

*/

fun getWidgetLayoutParams(): WindowManager.LayoutParams? {

if (layoutParams == null) {

layoutParams = WindowManager.LayoutParams()

layoutParams!!.type = WindowManager.LayoutParams.TYPE_PHONE

layoutParams!!.format = PixelFormat.RGBA_8888

layoutParams!!.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE

layoutParams!!.gravity = Gravity.LEFT or Gravity.TOP

layoutParams!!.x = windowManager!!.defaultDisplay.width

layoutParams!!.y =0

layoutParams!!.width = SuspensionWindowLayout.widget_width

layoutParams!!.height = SuspensionWindowLayout.widget_height

}

return layoutParams

}

/**

* 获取窗口管理器

*/

fun getWindowManager(context: Context): WindowManager ?{

if (windowManager == null) {

windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager

}

return windowManager

}

}

}

4. 开启一个悬浮窗口:

先进行判断,若是悬浮弹窗为未开启,则进行开启。

import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

//点击开启悬浮窗口

main_open_window.setOnClickListener {

requestPermission()

}

}

/**

* 开启悬浮弹窗

*/

fun openSuspensionWindow(){

//未开启窗口,则开启

if (!SuspensionWindowManagerUtils.windowIsOpen()) {

SuspensionWindowManagerUtils.createSuspensionWindow(applicationContext)

}

}

}

5. 在AndroidManifest.xml中添加系统弹窗权限:

6. Android 5.1及其以下系统,运行效果:

在模拟器上运行无问题,但是在红米手机上出现问题。

红米手机需要先开启悬浮权限:显示悬浮窗–>允许。

819f1601c4922aa0dedfbaedbf992ac6.png

录制效果如下:

ce9b7db09ff2b9892a9200c7a4fa5706.gif

授权 SYSTEM_ALERT_WINDOW Permission 在 android 6.0 及其以上版本的系统

运行设备:

AndroidStudio 自带的模拟器,其API 24。

运行结果:

在输出台上提示以下错误:

Caused by: android.view.WindowManager$BadTokenException: Unable to add window

android.view.ViewRootImpl$W@b9261f -- permission denied for window type 2002

at android.view.ViewRootImpl.setView(ViewRootImpl.java:702)

at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:342)

at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)

查看SYSTEM_ALERT_WINDOW权限,可知:

若是运用程序的目标API在23及其以上,程序需要通过权限管理界面,开启授权。程序发送ACTION_MANAGE_OVERLAY_PERMISSION的动作,使用Settings.canDrawOverlays()来检查是否授权。

解决方式:

1. 检查权限:

使用Settings.canDrawOverlays()来检查是否授权。

/**

* 当目标版本大于23时候,检查权限

*/

fun checkPermission():Boolean{

if (Build.VERSION.SDK_INT>=23)

return Settings.canDrawOverlays(this)

else

return true

}

2. 用户授权:

发送ACTION_MANAGE_OVERLAY_PERMISSION,开启权限授权界面,用户授权,允许悬浮在运用程序之上。

/**

* 申请权限的状态code

*/

var request_code=1

/**

* 开启权限管理界面,授权。

*/

fun requestPermission(){

var intent=Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName"))

startActivityForResult(intent,request_code)

}

3. 响应授权结果:

使用Settings.canDrawOverlays()来检查授权结果,用户在管理界面是否授权。

/**

* 回调申请结果

*/

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

when(requestCode) {

request_code -> {

if (checkPermission()) { //用户授权成功

openSuspensionWindow()

} else { //用户拒绝授权

Toast.makeText(application, "弹窗权限被拒绝", Toast.LENGTH_SHORT).show()

}

}

}

super.onActivityResult(requestCode, resultCode, data)

}

4. 在Android6.0及其以上,运行效果:

在7.0系统模拟器上,API24运行项目,效果如下

c8639f137ead14e9c7c4e2b4e27ccb20.gif

项目代码:https://github.com/13767004362/SuspensionWindowDemo

资源参考:

郭大大的教程:http://blog..net/guolin_blog/article/details/8689140/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/351359.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

谷歌guava_Google Guava BloomFilter

谷歌guava当Guava项目发布版本11.0时,新添加的功能之一是BloomFilter类。 BloomFilter是唯一的数据结构,用于指示元素是否包含在集合中。 使BloomFilter有趣的是,它将指示元素是否绝对不包含或可能包含在集合中。 永远不会出现假阴性的特性使…

CMDB学习之三数据采集

判断系统因为是公用的方法,所有要写基类方法使用,首先在插件中创建一个基类 将插件文件继承基类 思路是创建基类使用handler.cmd ,命令去获取系统信息,然后进行判断,然后去执行 磁盘 ,cpu,网卡,…

linux下挂载iso镜像的方法

新建目录/mnt/cdrom 执行命令 mount /dev/cdrom /mnt/cdrom 12[rootocdp1 cdrom]# mount /dev/cdrom /mnt/cdrommount: /dev/sr0 写保护,将以只读方式挂载进入/mnt/cdrom文件夹下发现里面是空的,说明挂载失败。 现在很多发行版中,光驱都不是/…

android onitemclicklistener 参数,android – OnItemClickListener从模型中获取数据

我是Android开发的新手,我正在尝试构建一个ListView,它使用gson从Web服务获取数据.我有一个模型类,一个列表类,一个适配器类和活动类.该列表工作正常,它获得了数据,现在我想将OnItemClickListener集成到它并将数据传递给第二个活动.我想得到项ID(DistrictId)并将其传递给下一个…

Java 8:对速度3.0.1“森林”流ORM的更深入了解

沿着这条路 我一直在为开源项目Speedment (它是Stream ORM Java Toolkit和Runtime)做出贡献,并且刚刚发布了一个新的主要版本3.0.1“ Forest”。 版本的发布以加利福尼亚州帕洛阿尔托的大街小巷命名,大多数贡献者都在这里工作。 沿…

读《人月神话》有感

翻开《人月神话》这本书,我感觉看这本与我们学的相关的书不相似,书中用了很多的形象的比喻,来阐述项目管理中的一些问题,让人以很轻松愉悦心态去阅读。书开始就形象有有趣的把软件危机比作:焦油坑。让我感觉到&#xf…

android 弹窗in,Android监听程序处于INACTIVITY(未操作状态)时间并作出相应的操作

最近遇到一个需求,app五分钟未操作需要返回到主页面。一开始就想到去监听onTouch和onClick事件,这个方法看似可行,但是实际操作起来还是很繁琐的,一不小心就达不到需要的效果。然后就果断放弃了。后来看api发现activity中有一个叫…

用gdb调试nasm汇编程序

对于一个程序员来说,调试是很重要的,可以节约找到bug的时间,不过以前在linux下一直是对c进行调试的,今天突然要对汇编进行调试还真不知道怎么调,特别是对linux下调试汇编程序基本没搞过。记得以前上课学masm时&#xf…

android 防腾讯新闻标题栏,仿腾讯视频android客户端上方的标题栏

如图,上方的导航栏是不是很酷,首先它本身是可以滑动的,然后右侧有一个可以查看所有Tab的按钮,然后他和下方的ViewPager还是联动的,可以通过点击它切换ViewPager,并且ViewPager滑动,它也会随着改…

链接克隆 完整克隆_深入克隆

链接克隆 完整克隆在继续克隆概念之前,让我们用对象创建概念刷新基础知识。 使用new运算符创建对象时,对象将在堆中获取内存分配。 堆中的对象创建 在Java中,理想情况下仅通过引用变量修改对象,即仅复制对象的内存地址&#xff…

linux系统调用和库函数调用的区别

Linux下对文件操作有两种方式:系统调用(system call)和库函数调用(Library functions)。可以参考《Linux程序设计》(英文原版为《Beginning Linux Programming》,作者是Neil Matthew和Richard S…

如果您在2016年编写过Java代码-这是您不容错过的趋势

2016年最有趣的Java相关主题 关于代码,有很多热门话题,而要跟上所有事情,这是一项全职的工作。 如果您想知道如何从谷壳中分离出小麦,我们已经为您完成了工作。 在下面的文章中,我们将介绍2016年最热门的内容&#x…

第三届蓝桥杯省赛---第39级台阶

第39级台阶 小明刚刚看完电影《第39级台阶》,离开电影院的时候,他数了数礼堂前的台阶数,恰好是39级! 站在台阶前,他突然又想着一个问题: 如果我每一步只能迈上1个或2个台阶。先迈左脚,然后左右交替&#xf…

android 视图覆盖,如何在Android中添加覆盖视图超过其他视图?

设为背景!只是开玩笑…你需要的是把你的意见放在一个RelativeLayout里.会有什么工作:xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"fill_parent"android:layout_height"fill_parent"&…

Linux内核源码分析方法

一、内核源码之我见 Linux内核代码的庞大令不少人“望而生畏”,也正因为如此,使得人们对Linux的了解仅处于泛泛的层次。如果想透析Linux,深入操作系统的本质,阅读内核源码是最有效的途径。我们都知道,想成为优秀的程序…

深度学习框架Keras介绍及实战

Keras 是一个用 Python 编写的高级神经网络 API,它能够以 TensorFlow, CNTK, 或者 Theano 作为后端运行。Keras 的开发重点是支持快速的实验。能够以最小的时延把你的想法转换为实验结果,是做好研究的关键。 本文以Kaggle上的项目:IMDB影评情感分析为例,…

操作系统--处理机调度

4 处理机调度 4.1 调度类型 操作系统必须为多个进程的竞争请求分配计算机资源。处理机调度的任务就是选出待分派的作业或进程,为之分配处理机。 为了便于处理机调度管理,通常在处理机调度中采用分级调度方式,其中包括以下3级调度:…

permgen_什么是PermGen泄漏?

permgen接下来是对Java应用程序中特定类型的内存问题的实用介绍。 即–我们将分析导致java.lang.OutOfMemoryError:PermGen空间的错误 堆栈跟踪中的症状。 首先,我们将介绍理解该主题所需的核心概念,并解释什么是对象,类&#…

TP、PHP同域不同子级域名共享Session、单点登录

TP、PHP同域不同子级域名共享Session、单点登录 目的: 为了部署同个域名下不同子级域名共享会话,从而实现单点登录的问题,一处登录,同域处处子系统即可以实现自动登录。 PHP支持通过设置cookie使得同域不同子域共享SESSION 1. 通…

html语言书写注意事项,HTML注意事项(学习笔记)

1、在所有浏览器中都是有效的,但使用 其实是更长远的保障。类似的标签也一样2、标签最好用小写,未来的版本中可能强制用小写3、标签属性始终为属性值加引号属性值应该始终被包括在引号内。双引号是最常用的,不过使用单引号也没有问题。在某些…