1、需求

首先说一下需求吧,看看是不是你们想要的:
在IM音视频聊天页面,点击页面上的最小化按钮,将Activity最小化并开启一个悬浮窗,悬浮窗可拖动可点击,点击进入当前通话页面。

2、思路

1、听到开启悬浮窗,第一时间就想到了要开启一个Service,在Service中进行布局的设置及拖动、点击逻辑的设定。
2、通话界面点击最小化按钮,将Activity最小化并保持后台运行,同时开启Service展示悬浮窗

3、实现

1、创建FloatVideoWindowService继承Service类
2、实现父类抽象方法
3、在onCreate()中进行布局设置,并获得WindowManager对象。
4、在onStartCommand()方法中将布局、参数添加到WindowManager中
5、增加全局变量boolean isStarted记录悬浮窗是否已打开。
6、在onDestroy()中将布局从WindowManager中移除
7、在onStartCommand()中进行控件的点击、拖动监听
8、点击最小化按钮执行moveTaskToBack(true),并启动Service,isStarted = true;
9、onRestart()中关闭Service;

以下是代码

Service.class

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.SystemClock;
import android.provider.Settings;
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.Button;
import android.widget.TextView;public class FloatVideoWindowService extends Service {private WindowManager mWindowManager;private WindowManager.LayoutParams wmParams;private LayoutInflater inflater;public static boolean isStarted = false;//viewprivate View mFloatingLayout;    //布局View@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();isStarted = true;initWindow();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {showFloatingWindow();return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {super.onDestroy();if (mWindowManager != null) {mWindowManager.removeView(mFloatingLayout);isStarted = false;}}/*** 设置悬浮框基本参数(位置、宽高等)*/private void initWindow() {mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);wmParams = getParams();inflater = LayoutInflater.from(getApplicationContext());mFloatingLayout = inflater.inflate(R.layout.layout_window, null);}private WindowManager.LayoutParams getParams() {wmParams = new WindowManager.LayoutParams();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;} else {wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;}wmParams.format = PixelFormat.RGBA_8888;//设置可以显示在状态栏上wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;//这是悬浮窗居中位置wmParams.gravity = Gravity.LEFT | Gravity.TOP;//70、210是我项目中的位置哦wmParams.x = 70;wmParams.y = 210;return wmParams;}private void showFloatingWindow() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//判断系统版本if (Settings.canDrawOverlays(this)) {mFloatingLayout.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(getApplicationContext(), VoiceCallActivity.class);startActivity(intent);}});mFloatingLayout.setOnTouchListener(new FloatingListener());mWindowManager.addView(mFloatingLayout, wmParams);}} else {mFloatingLayout.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(getApplicationContext(), VoiceCallActivity.class);startActivity(intent);}});mFloatingLayout.setOnTouchListener(new FloatingListener());mWindowManager.addView(mFloatingLayout, wmParams);}}private int mTouchStartX, mTouchStartY, mTouchCurrentX, mTouchCurrentY;private int mStartX, mStartY, mStopX, mStopY;private boolean isMove;private class FloatingListener implements View.OnTouchListener {@Overridepublic boolean onTouch(View v, MotionEvent event) {int action = event.getAction();switch (action) {case MotionEvent.ACTION_DOWN:isMove = false;mTouchStartX = (int) event.getRawX();mTouchStartY = (int) event.getRawY();mStartX = (int) event.getX();mStartY = (int) event.getY();break;case MotionEvent.ACTION_MOVE:mTouchCurrentX = (int) event.getRawX();mTouchCurrentY = (int) event.getRawY();wmParams.x += mTouchCurrentX - mTouchStartX;wmParams.y += mTouchCurrentY - mTouchStartY;mWindowManager.updateViewLayout(mFloatingLayout, wmParams);mTouchStartX = mTouchCurrentX;mTouchStartY = mTouchCurrentY;break;case MotionEvent.ACTION_UP:mStopX = (int) event.getX();mStopY = (int) event.getY();if (Math.abs(mStartX - mStopX) >= 1 || Math.abs(mStartY - mStopY) >= 1) {isMove = true;}break;}return isMove;}}
}
Activity.class
private Intent serviceIntent;private static HomeWatcherReceiver mHomeKeyReceiver = null;public void openMinWindow() {if (!FloatVideoWindowService.isStarted) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//判断系统版本if (!Settings.canDrawOverlays(this)) {Toast.makeText(this, "当前无权限,请授权", Toast.LENGTH_SHORT);startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), 0);} else {serviceIntent = new Intent(VoiceCallActivity.this, FloatVideoWindowService.class);startService(serviceIntent);moveTaskToBack(true);}} else {serviceIntent = new Intent(VoiceCallActivity.this, FloatVideoWindowService.class);startService(serviceIntent);moveTaskToBack(true);}}}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {if (requestCode == 0) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {if (!Settings.canDrawOverlays(this)) {Toast.makeText(this, "授权失败", Toast.LENGTH_SHORT).show();} else {Toast.makeText(this, "授权成功", Toast.LENGTH_SHORT).show();serviceIntent = new Intent(VoiceCallActivity.this, FloatVideoWindowService.class);startService(serviceIntent);moveTaskToBack(true);}}}}@Overrideprotected void onRestart() {super.onRestart();if (serviceIntent != null) {stopService(serviceIntent);}}//以下为Home键监听,最小化到桌面时也让悬浮窗启动@Subscribepublic void onEvent(String event) {if (event.equals("startService")) {openMinWindow();}}@Overrideprotected void onResume() {super.onResume();registerHomeKeyReceiver(this);}@Overrideprotected void onPause() {super.onPause();unregisterHomeKeyReceiver(this);}private static void registerHomeKeyReceiver(Context context) {mHomeKeyReceiver = new HomeWatcherReceiver();final IntentFilter homeFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);context.registerReceiver(mHomeKeyReceiver, homeFilter);}private static void unregisterHomeKeyReceiver(Context context) {if (null != mHomeKeyReceiver) {context.unregisterReceiver(mHomeKeyReceiver);}}
HomeWatcherReceiver.class (手机底部按键监听)
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import de.greenrobot.event.EventBus;/*** PROJECT_NAME:meteor_dog_android* Created by ENZO on 2018/10/19,16:18* Description: Home键监听*/public class HomeWatcherReceiver extends BroadcastReceiver {private static final String LOG_TAG = "HomeReceiver";private static final String SYSTEM_DIALOG_REASON_KEY = "reason";private static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";private static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";private static final String SYSTEM_DIALOG_REASON_LOCK = "lock";private static final String SYSTEM_DIALOG_REASON_ASSIST = "assist";@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();Log.i(LOG_TAG, "onReceive: action: " + action);if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);Log.i(LOG_TAG, "reason: " + reason);if (SYSTEM_DIALOG_REASON_HOME_KEY.equals(reason)) {//发送通知,启动ServiceEventBus.getDefault().post("startService");}else if (SYSTEM_DIALOG_REASON_RECENT_APPS.equals(reason)) {// 长按Home键 或者 activity切换键}else if (SYSTEM_DIALOG_REASON_LOCK.equals(reason)) {// 锁屏}else if (SYSTEM_DIALOG_REASON_ASSIST.equals(reason)) {// samsung 长按Home键}}}}

4、注意

4.1: Service记得在AndroidManifest.xml中注册哦
4.2: 6.0以上(23)判断是否有悬浮窗权限请用Settings.canDrawOverlays(this)方法
4.3: 需要添加权限 android.permission.SYSTEM_ALERT_WINDOW
4.4: Eventbus记得解除注册啊!!我深有体会[尴尬]

android悬浮窗(支持退回桌面后显示)相关推荐

  1. android显示二维毫秒,Android 悬浮窗显示毫秒级时间

    Android 悬浮窗显示毫秒级时间Android 悬浮窗显示毫秒级时间. 运行效果如下: 1.新建工程"FloatWindowDemo2", 工程如下: 2. "Mai ...

  2. android显示二维毫秒,Android应用开发Android 悬浮窗显示毫秒级时间

    本文将带你了解Android应用开发Android 悬浮窗显示毫秒级时间,希望本文对大家学Android有所帮助. Android   悬浮窗显示毫秒级时间Android 悬浮窗显示毫秒级时间. 运行 ...

  3. android悬浮窗语音识别demo

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

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

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

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

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

  6. Android 悬浮窗语音识别功能开发详解

    笔者是一个普通不能再普通的程序员,本着出处兴趣,花时间研究了一下,想实现手机的悬浮窗语音识别功能,这样不影响自己其它操作的,语音识别技术是用百度云语音sdk,应该不难实现,很难实现就是核心语音识别技术 ...

  7. Android 悬浮窗功能的实现

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

  8. Android悬浮窗的简单实现

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

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

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

最新文章

  1. 赞!经典教材《统计学习导论》终于有Python版了
  2. 亿级别记录的mongodb分页查询java代码实现
  3. Spring-AOP 静态正则表达式方法匹配切面
  4. python中、变量指向的对象可以发生变化吗_python中的引用传递,可变对象,不可变对象,list注意点...
  5. 夫妻两一个两年内3张卡9次逾期,一人4次,还能办理房贷吗?
  6. 2018 年你需要知道的 11 个 JavaScript 库
  7. LNMP1.4环境中安装fileinfo插件
  8. Sqlite大数据量查询优化比较-转
  9. excel转PDF时,画图不分页
  10. oracle client 客户端 安装 oracle客户端安装
  11. leetcode每日一题2021.7.14【1818. 绝对差值和】
  12. [FOI2020]楼房搭建
  13. CTGU实验6_2-创建借书存储过程
  14. Word文档快速调整表格列宽度
  15. 废品站老板切割金属罐体时发生爆炸致死
  16. 基于Python的银行信用卡欺诈预测模型设计 文档+任务书+项目源码及数据
  17. Arthas(阿尔萨斯)使用
  18. 机器学习中的随机过程(伯努利过程与泊松过程)
  19. 千兆网络变压器原理图及网络变压器线圈各磁环定义
  20. 无人机航测作业流程,你会几个?

热门文章

  1. python开发工程师是什么级别_什么条件才能成为Python开发工程师
  2. Ubuntu使用小教程02——服务器上安装ubuntu桌面版(新手向)
  3. un1que成员介绍
  4. java安卓字体_Android中添加外部字体库和竖直排列字体
  5. 宝宝起名神器小程序源码_支持多种流量主模式
  6. 贝尔曼方程基于全期望公式的前期推导
  7. WSL下源码阅读工具LXR(Linux Cross-Referencer)的配置
  8. C语言参数传递——地址传递与值传递
  9. with open新创建的文件在哪找_Python 基本功: 4. 读写文件
  10. win10 更新计算机时间,win10更新时间太长怎么回事_windows10更新时间太久解决教程...