今天给大家写一个这个Android悬浮窗的功能,这个功能一般在360,酷狗(桌面歌词),网易云音乐(桌面歌词)上面用到,一般开发是很少碰到这个功能的,但是这个悬浮窗功能可以帮助理解Android的绘制机制。

这个功能大概是这样的,我们进一个Activity,在onCreate里面开启一个Service,在服务器里添加一个悬浮窗,然后退出Activity.


Activity:

package com.example.library;import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.widget.Toast;
import com.example.library.service.MainService;public class SecondActivity extends BaseActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//不需要setContentViewif (Build.VERSION.SDK_INT >= 23) {if (Settings.canDrawOverlays(SecondActivity.this)) {Intent intent = new Intent(SecondActivity.this, MainService.class);Toast.makeText(SecondActivity.this,"已开启Toucher",Toast.LENGTH_SHORT).show();startService(intent);finish();} else {//若没有权限,提示获取.Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);Toast.makeText(SecondActivity.this,"需要取得权限以使用悬浮窗",Toast.LENGTH_SHORT).show();startActivity(intent);}} else {//SDK在23以下,不用管.Intent intent = new Intent(SecondActivity.this, MainService.class);startService(intent);finish();}}}

然后再是Service:

package com.example.library.service;import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.example.library.R;public class MainService extends Service {//Log用的TAGprivate static final String TAG = "MainService";//布局参数.WindowManager.LayoutParams params;//实例化的WindowManager.WindowManager windowManager;RelativeLayout toucherLayout;ImageButton imageButton1;//状态栏高度.(接下来会用到)int statusBarHeight = -1;@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();Log.i("MainService" , "onCreate==onCreate");//OnCreate中来生成悬浮窗.createToucher();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.i("MainService" , "onCreate==onStartCommand");return super.onStartCommand(intent, flags, startId);}private void createToucher(){//赋值WindowManager&LayoutParam.params = new WindowManager.LayoutParams();windowManager = (WindowManager)getBaseContext(). getSystemService(Context.WINDOW_SERVICE);//设置type.系统提示型窗口,一般都在应用程序窗口之上.//这个是8.0系统以下的
//        params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;//这个是8.0系统以上params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;//设置效果为背景透明.params.format = PixelFormat.RGBA_8888;//设置flags.不可聚焦及不可使用按钮对悬浮窗进行操控.params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;//设置窗口初始停靠位置.params.gravity = Gravity.LEFT | Gravity.TOP;params.x = 0;params.y = 0;//设置悬浮窗口长宽数据.//注意,这里的width和height均使用px而非dp.这里我偷了个懒//如果你想完全对应布局设置,需要先获取到机器的dpi//px与dp的换算为px = dp * (dpi / 160).params.width = WindowManager.LayoutParams.MATCH_PARENT;params.height = 300;LayoutInflater inflater = LayoutInflater.from(getApplication());toucherLayout = (RelativeLayout) inflater.inflate(R.layout.layout_main , null);imageButton1 = toucherLayout.findViewById(R.id.imageView);//主动计算出当前View的宽高信息.
//        toucherLayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);//用于检测状态栏高度.int resourceId = getResources().getIdentifier("status_bar_height","dimen","android");if (resourceId > 0){statusBarHeight = getResources().getDimensionPixelSize(resourceId);}Log.i(TAG,"状态栏高度为:" + statusBarHeight);//其他代码...imageButton1.setOnClickListener(new View.OnClickListener() {long[] hints = new long[2];@Overridepublic void onClick(View v) {Log.i(TAG,"点击了");System.arraycopy(hints,1,hints,0,hints.length -1);hints[hints.length -1] = SystemClock.uptimeMillis();if (SystemClock.uptimeMillis() - hints[0] >= 700){Log.i(TAG,"要执行");Toast.makeText(MainService.this,"连续点击两次以退出", Toast.LENGTH_SHORT).show();}else{Log.i(TAG,"即将关闭");stopSelf();}}});imageButton1.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {//ImageButton我放在了布局中心,布局一共300dpparams.x = (int) event.getRawX() - 150;//这就是状态栏偏移量用的地方params.y = (int) event.getRawY() - 150 - statusBarHeight;windowManager.updateViewLayout(toucherLayout,params);return false;}});windowManager.addView(toucherLayout , params);}@Overridepublic void onDestroy(){if (windowManager != null){windowManager.removeView(toucherLayout);}super.onDestroy();}
}

然后配置Service

然后就是权限配置

然后就是去手机系统设置打开:在其他应用上层显示
然后效果出来啦


以上就是一个简单的悬浮窗口功能 说到这里就不难明白,Activity并不是我们唯一显示布局的组件,这个是为什么,原来我们的Activity只是一个用来承载我们布局的中间件,它本身不负责绘制,我们写的xml文件是在Window对象里面执行的,Window的实现类是PhoneWindow,PhoneWindow里面有一个WindowManager,然后把我们的View传给WinowManagerImpl,再传给WindowManagerGlobal,再传给ViewRootImpl,ViewRootImpl里面负责事件处理跟图像绘制,所以Activity其实也是把view给WindowManager处理了,只是Activity会负责整个视图的生命周期。这个悬浮窗就是脱离了Activity的限制。

在这里我们设置了系统窗口类型,只是系统管制了,需要手动设置权限而已。

一个Activity里有一个Window对象,这个Window对象就包含了布局,这个Window对象必须依附Activity,我们添加Dialog的时候其实就是加一个Window对象,所以我们在新建Dialog的时候不能传Context,而必须传Activity实例对象的原因。

像我们的事件分发机制,为什么是Activity–>ViewGroup–>View的流程,也就有了答案,因为点击事件是由底层硬件发出的,而Activity是我们承载布局的组件(中间件),从底层往上传递,肯定是Activity在前面。




Android悬浮窗相关推荐

  1. android悬浮窗语音识别demo

    带有android悬浮窗的语音识别语义理解demo 如发现代码排版问题,请访问CSDN博客 Android桌面悬浮窗实现比较简单,本篇以一个语音识别,语义理解的demo来演示如何实现android悬浮 ...

  2. Android 悬浮窗功能的实现

    前言 我们大多数在两种情况下可以看到悬浮窗,一个是视频通话时的悬浮窗,另一个是360卫士的悬浮球,实现此功能的方式比较多,这里以视频通话悬浮窗中的需求为例.编码实现使用Kotlin.Java版本留言邮 ...

  3. Android悬浮窗的简单实现

    1. 前言 现在很多应用都有小悬浮窗的功能,比如看直播的时候,通过Home键返回桌面,直播的小窗口仍可以在屏幕上显示.下面将介绍下悬浮窗的的一种简单实现方式. 2.原理 Window我们应该很熟悉,它 ...

  4. Android悬浮窗的实现

    Android悬浮窗的实现 *本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 本文也发布于本人的知乎文章:https://zhuanlan.zhihu.com/p/39421112 ...

  5. Android悬浮窗原理解析(Window)[源码]

    悬浮窗,在大多数应用中还是很少见的,目前我们接触到的悬浮窗,差不多都是一些系统级的应用软件,例如:360安全卫士,腾讯手机管家等:在某些服务行业如金融,餐饮等,也会在应用中添加悬浮窗,例如:美团的偷红 ...

  6. 安卓java浮层不响应点击事件,Android悬浮窗屏蔽悬浮窗外部所有的点击事件的实例代码...

    Android可以在所有应用上方添加View,就是给WindowManager添加一个View,在创建的View的时候可以给这个View设置LayoutParams(android.view.Wind ...

  7. Android展开悬浮窗功能,Android 悬浮窗 (附圆形菜单悬浮窗)

    序言 Android悬浮窗的实现,主要有四个步骤: 1. 声明及申请权限 2. 构建悬浮窗需要的控件 3. 将控件添加到WindowManager 4. 必要时更新WindowManager的布局 一 ...

  8. android动态获取悬浮窗,Android 悬浮窗实现

    Android悬浮窗实现中需要注意的两点是 1.Android 6.0之后的悬浮窗动态申请 2.Window 的type属性在Android8.0前后的适配 public abstract class ...

  9. Android悬浮窗适配全机型,包含8.0,小米魅族华为悬浮窗权限适配demo看这一篇就够了

    机型多杂,适配无法完全兼容,不如换种实现方式,性能比悬浮窗好,不需要权限,效果更好:https://blog.csdn.net/m0_38058826/article/details/10399339 ...

  10. Android悬浮窗开启 适配所有机型(附源码)

    Android悬浮窗开启 适配所有机型(附源码) 1.开启悬浮窗权限 清单文件中添加: <uses-permission android:name="android.permissio ...

最新文章

  1. LiFi会将大数据和物联网带到新高度吗?
  2. v-for 切换不同的class
  3. html中怎么选择相同的代码,不同的HTML,相同的代码隐藏和相同的控件
  4. insert事务隔离mysql_MySQL数据库详解(三)MySQL的事务隔离剖析
  5. Feature Engineering 特征工程 3. Feature Generation
  6. mysql 中序号要怎么写_如何在mysql的字段ID中插入自动编号?
  7. 1.2. Cisco IOS Firewall
  8. Python爬虫的智能化解析——Diffbot
  9. 机器学习基石笔记-Lecture 14 Regularization
  10. 【转】ASPNET程序中常用的三十三种代码
  11. fiddler 查看接口响应时间
  12. 2022年第十三届蓝桥杯省赛--难度评价
  13. 10款提高工作效率的工具软件,你值得拥有!
  14. 卷积神经网络学习路线(十九) | 旷世科技 2017 ShuffleNetV1
  15. 红牛v5 android cm12.1 分辨率修改
  16. SQL之to_date()
  17. dll注册加载失败解决方法
  18. 湖南云畅网络科技有限公司携手伙伴,共筑长三角数字经济产业新高地
  19. mysql求每个班级的最高分_sql查询每个班上成绩最高的学生信息
  20. c语言字母转换数字代码,实现c语言中字符串和数字的相互转换的代码

热门文章

  1. GitHub标星1w的安卓架构师必备技能,终获offer
  2. 农场游戏开发记录十七(控制台版本完成)
  3. python好用的库存尾货女装_服装库存尾货生意怎么做?
  4. JS 基础交互(交互三部曲)
  5. 【paper】VPGNet 论文浅析
  6. qq空间个人档html代码,qq空间个人档签名
  7. imp-00017: 由于 oracle 错误 6550,数据库IMP导入时常见错误说明-转自百度空间-三生有约...
  8. ACREL-3000电能管理系统在金桥德勤电能管理系统项目中的应用
  9. 凭这份Java面试宝典,我成功拿到阿里、腾讯、百度、字节等六家大厂offer!
  10. 《仙剑奇侠传三》游戏分析