在一些场合里,我们使用悬浮窗会有很大的便利,比如IOS系统的悬浮窗,360或者其他手机卫士的悬浮窗等等。

本篇博客,我们创造出两个悬浮窗,通过点击小悬浮窗打开或者关闭大悬浮窗。

代码如下:

在这之前,我们需要在manifest中申请权限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
1
并且,悬浮窗这个权限我们需要手动在手机找到应用权限管理,允许这个权限才行
 小悬浮窗的界面代码float_normal_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/ll_float_normal"android:gravity="center"android:orientation="vertical"><ImageViewandroid:id="@+id/iv_show_control_view"android:layout_gravity="center"android:background="@drawable/float_bg"android:layout_width="65dp"android:orientation="vertical"android:alpha="0.3"android:layout_height="65dp" ></ImageView></LinearLayout>

大悬浮窗的界面代码float_window_big.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/big_window_layout"android:layout_width="200dip"android:layout_height="100dip"android:background="@android:color/holo_green_dark"android:orientation="vertical"><Buttonandroid:id="@+id/close"android:layout_width="100dip"android:layout_height="40dip"android:layout_gravity="center_horizontal"android:layout_marginTop="12dip"android:text="关闭悬浮窗"/><Buttonandroid:id="@+id/back"android:layout_width="100dip"android:layout_height="40dip"android:layout_gravity="center_horizontal"android:text="主页"/>
</LinearLayout>

入口activity(MainActivity ):

public class MainActivity extends Activity {MyWindowManager myWindowManager;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {if(!Settings.canDrawOverlays(getApplicationContext())) {//启动Activity让用户授权Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);intent.setData(Uri.parse("package:" + getPackageName()));startActivityForResult(intent,100);}}setContentView(R.layout.activity_main);myWindowManager = MyWindowManager.getInstance();myWindowManager.createNormalView(this.getApplicationContext());}}

悬浮窗管理器MyWindowManager:

public class MyWindowManager {private FloatNormalView normalView;private Context mContext;private static MyWindowManager instance;private WindowManager windowManager;private MyWindowManager() {}public static MyWindowManager getInstance() {if (instance == null)instance = new MyWindowManager();return instance;}private WindowManager getWindowManager(Context context) {if (windowManager == null)windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);mContext=context;return windowManager;}/*** 判断小悬浮窗是否存在** @return*/public boolean isNormalViewExists() {return normalView != null;}/*** 创建小型悬浮窗*/public void createNormalView(Context context) {if (normalView == null) {normalView = new FloatNormalView(context);}}/*** 移除悬浮窗** @param context*/public void removeNormalView(Context context) {if (normalView != null) {windowManager.removeView(normalView);normalView = null;}}FloatWindowBigView bigWindow;private WindowManager.LayoutParams bigWindowParams;public void createBigWindow(Context context) {WindowManager windowManager = getWindowManager(context);int screenWidth = windowManager.getDefaultDisplay().getWidth();int screenHeight = windowManager.getDefaultDisplay().getHeight();if (bigWindow == null) {bigWindow = new FloatWindowBigView(context);if (bigWindowParams == null) {bigWindowParams = new WindowManager.LayoutParams();bigWindowParams.x = screenWidth / 2 - FloatWindowBigView.viewWidth / 2;bigWindowParams.y = screenHeight / 2 - FloatWindowBigView.viewHeight / 2;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {bigWindowParams.type =WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;}else {bigWindowParams.type = WindowManager.LayoutParams.TYPE_PHONE;}bigWindowParams.format = PixelFormat.RGBA_8888;bigWindowParams.gravity = Gravity.LEFT | Gravity.TOP;bigWindowParams.width = FloatWindowBigView.viewWidth;bigWindowParams.height = FloatWindowBigView.viewHeight;}windowManager.addView(bigWindow, bigWindowParams);}}public void removeBigWindow(Context context) {if (bigWindow != null) {WindowManager windowManager = getWindowManager(context);windowManager.removeView(bigWindow);bigWindow = null;}}
}

小悬浮窗FloatNormalView:

public class FloatNormalView extends LinearLayout implements View.OnTouchListener {private final static String TAG="FloatNormalView";/*** 记录小悬浮窗的宽度*/public static int viewWidth;/*** 记录小悬浮窗的高度*/public static int viewHeight;/*** 记录系统状态栏的高度*/private static int statusBarHeight;/*** 用于更新小悬浮窗的位置*/private WindowManager windowManager;/*** 小悬浮窗的参数*/private WindowManager.LayoutParams mParams;/*** 记录当前手指位置在屏幕上的横坐标值*/private float xInScreen,xInitScreen;/*** 记录当前手指位置在屏幕上的纵坐标值*/private float yInScreen,yInitScreen;/*** 记录手指按下时在屏幕上的横坐标的值*/private float xDownInScreen;/*** 记录手指按下时在屏幕上的纵坐标的值*/private float yDownInScreen;/*** 记录手指按下时在小悬浮窗的View上的横坐标的值*/private float xInView;/*** 记录手指按下时在小悬浮窗的View上的纵坐标的值*/private float yInView;public FloatNormalView(Context context) {super(context);windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);LayoutInflater.from(context).inflate(R.layout.float_normal_view, this);View view = findViewById(R.id.ll_float_normal);viewWidth = view.getLayoutParams().width;viewHeight = view.getLayoutParams().height;initLayoutParams();}OnClickListener circleClickListener=new OnClickListener() {@Overridepublic void onClick(View view) {MyWindowManager.getInstance().createBigWindow(getContext());MyWindowManager.getInstance().removeNormalView(getContext());}};/*** 初始化参数*/private void initLayoutParams() {//屏幕宽高int screenWidth = windowManager.getDefaultDisplay().getWidth();int screenHeight = windowManager.getDefaultDisplay().getHeight();mParams = new WindowManager.LayoutParams();// FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口// FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按,不设置这个flag的话,home页的划屏会有问题mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;//悬浮窗默认显示的位置mParams.gravity = Gravity.START | Gravity.TOP;//指定位置mParams.x = screenWidth - viewWidth * 2;mParams.y = screenHeight / 2 + viewHeight * 2;//悬浮窗的宽高mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;mParams.format = PixelFormat.TRANSPARENT;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {mParams.type =WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;}else {mParams.type = WindowManager.LayoutParams.TYPE_PHONE;}mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;windowManager.addView(this, mParams);}@Overridepublic boolean onTouchEvent(MotionEvent event) {Log.i(TAG,"hsz--->onTouchEvent:"+event.getAction());switch (event.getAction()) {case MotionEvent.ACTION_DOWN:// 手指按下时记录必要数据,纵坐标的值都需要减去状态栏高度xInView =xInitScreen= event.getX();yInView =yInitScreen= event.getY();xDownInScreen = event.getRawX();yDownInScreen = event.getRawY() - getStatusBarHeight();xInScreen = event.getRawX();yInScreen = event.getRawY() - getStatusBarHeight();break;case MotionEvent.ACTION_MOVE:xInScreen = event.getRawX();yInScreen = event.getRawY() - getStatusBarHeight();// 手指移动的时候更新小悬浮窗的位置updateViewPosition();break;case MotionEvent.ACTION_UP:// 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。if (xDownInScreen == xInScreen && yDownInScreen == yInScreen) {openOrCloseControlView();}Float distanceX=event.getX()-xInitScreen;Float distanceY=event.getY()-yInitScreen;if(distanceX==0 && distanceY==0){MyWindowManager.getInstance().createBigWindow(getContext());MyWindowManager.getInstance().removeNormalView(getContext());}break;default:break;}return true;}/*** 将小悬浮窗的参数传入,用于更新小悬浮窗的位置。** @param params 小悬浮窗的参数*/public void setParams(WindowManager.LayoutParams params) {mParams = params;}/*** 更新小悬浮窗在屏幕中的位置。*/private void updateViewPosition() {mParams.x = (int) (xInScreen - xInView);mParams.y = (int) (yInScreen - yInView);windowManager.updateViewLayout(this, mParams);}/*** 用于获取状态栏的高度。** @return 返回状态栏高度的像素值。*/private int getStatusBarHeight() {if (statusBarHeight == 0) {try {Class<?> c = Class.forName("com.android.internal.R$dimen");Object o = c.newInstance();Field field = c.getField("status_bar_height");int x = (Integer) field.get(o);statusBarHeight = getResources().getDimensionPixelSize(x);} catch (Exception e) {e.printStackTrace();}}return statusBarHeight;}@Overridepublic boolean onTouch(View v, MotionEvent event) {return false;}
}

大悬浮窗FloatWindowBigView:

public class FloatWindowBigView extends LinearLayout {/*** 记录大悬浮窗的宽度*/public static int viewWidth;/*** 记录大悬浮窗的高度*/public static int viewHeight;public FloatWindowBigView(final Context context) {super(context);LayoutInflater.from(context).inflate(R.layout.float_window_big, this);View view = findViewById(R.id.big_window_layout);viewWidth = view.getLayoutParams().width;viewHeight = view.getLayoutParams().height;Button close = (Button) findViewById(R.id.close);Button back = (Button) findViewById(R.id.back);close.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// 点击关闭悬浮窗的时候MyWindowManager.getInstance().removeBigWindow(context);MyWindowManager.getInstance().createNormalView(context);}});back.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// 点击主页的时候,移除大悬浮窗,创建小悬浮窗Intent intent = new Intent(Intent.ACTION_MAIN);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.addCategory(Intent.CATEGORY_HOME);context.startActivity(intent);MyWindowManager.getInstance().removeBigWindow(context);MyWindowManager.getInstance().createNormalView(context);}});}
}

Android之仿IOS悬浮窗相关推荐

  1. android 仿ios悬浮窗,iOS仿微信悬浮窗

    仿微信悬浮窗,可直接协议加入悬浮窗或者直接调用方法注册,可自定义转场动画 演示 myFloat.gif 用法1 在Appdelegate中注册 传入对应控制器的className //只带控制器的cl ...

  2. android 仿ios悬浮窗,iOS高仿微信悬浮窗、忍者小猪游戏、音乐播放器、支付宝、今日头条布局滚动效果等源码...

    iOS精选源码 iOS优质博客 之前看了很多面试题,感觉要不是不够就是过于冗余,于是我将网上的一些面试题进行了删减和重排,现在分享给大家.(题目来源于网络,侵删)1. Object-c的类可以多重继承 ...

  3. iOS高仿微信悬浮窗、忍者小猪游戏、音乐播放器、支付宝、今日头条布局滚动效果等源码...

    iOS精选源码 iOS WKWebView的使用源码 模仿apple music 小播放器的交互实现 高仿微信的悬浮小窗口 iOS仿支付宝首页效果 [swift]仿微信悬浮窗 类似于今日头条,网易新闻 ...

  4. 【Android】利用WindowsManager悬浮窗播放本地视频以及下载线上视频保存在本地

    基于目前又重新拾起了分屏的项目需求,对之前研究的分屏播放视频做了更深入的研究.在之前的基础上做了改进和用户优化上的处理,实现了原生的VideoView加载本地视频.并使用FileDownLoader下 ...

  5. Android安卓仿IOS音量调节-自定义view系列(4)

    Android安卓仿IOS音量调节-自定义view系列 功能简介 主要实现步骤 xml相关属性设置 java代码 Android技术生活交流 更多其他页面-自定义View-实用功能合集:点击查看 Gi ...

  6. Android高仿iOS Messages聊天气泡

    Android高仿iOS Messages聊天气泡 一.目标 二.功能分析 三.实现代码 1. ChatItem 2. DateItem 3. TextItem 4. PhotoItem 5. Cha ...

  7. Android高仿iOS Messages录音操作按钮

    Android高仿iOS Messages录音操作按钮 目录 一.目标 二.功能分析 三.实现效果 四.实现过程 五.开发过程回顾 六.接下来 七.Finally 前面的2次开发,分别完成了实现录音和 ...

  8. Android 摄像头预览悬浮窗,可拖动,可显示在其他app上方

    市面上常见的摄像头悬浮窗,如微信.手机QQ的视频通话功能,有如下特点: 整屏页面能切换到一个小的悬浮窗 悬浮窗能运行在其他app上方 悬浮窗能跳回整屏页面,并且悬浮窗消失 我们探讨过用CameraX打 ...

  9. android浮标权限管理,Android辅助权限与悬浮窗

    Android辅助权限与悬浮窗在执行自动化服务的流程中,我们其实并不希望被用户的操作中断流程,所以有什么方法在用户点击自动化操作的过程中,避免用户再次操作呢?那就是开启一个全局透明的悬浮窗,进行屏蔽触 ...

最新文章

  1. 修改系统UIAlertAction的按钮颜色
  2. xilinx FPGA的远程更新(动态加载)详解(Using a Microprocessor to Configure 7 Series FPGAs)
  3. 全志A33-linux内核early_printk分析及使用
  4. html,css颜色,色系
  5. php元类,什么是元类-python编程入门系列图文教程-PHP中文网教程
  6. css3 微信聊天小尖角,用CSS制作聊天框小尖角、气泡效果
  7. 利用python实现杜利特尔分解法
  8. Dragonfly软件电脑环境
  9. error LNK2005解决方法
  10. 常用计算方法(C语言代码)(计算方法课程)
  11. iOS 将http%3A%2F%2F解析为URL
  12. 启动优化·基础论·浅析 Android 启动优化
  13. html记不住单词,只需这4种方法,让你30分钟记住100单词
  14. 5阶无向完全图_第8章 几种典型图(5}.ppt
  15. 数字证书常见格式整理
  16. bcd码和16进制的区别
  17. 2021年三个季度的监控摄像头品牌排名
  18. 干货!面向人群计数的跨模态协作表征学习方法和大规模RBGT数据集
  19. hdu_5151_Sit sit sit(区间DP)
  20. 解决qtcreator无法启动

热门文章

  1. yshon对讲机如何调频率_对讲机怎样调频率?
  2. 如何实现IM即时通信系统(二)
  3. USB标准协议下载地址
  4. 算法与数据结构 判断选择程序填空 绪论
  5. 阿里云ECS云服务器购买流程
  6. UTC-5 EST 是哪儿的时间
  7. 超详细Linux下QT使用appimage打包程序
  8. 经纬恒润 标记重复元素 python
  9. java 多态性性(转帖合集)
  10. Word文档在文字上方添加横线的方法