Android之仿IOS悬浮窗
在一些场合里,我们使用悬浮窗会有很大的便利,比如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悬浮窗相关推荐
- android 仿ios悬浮窗,iOS仿微信悬浮窗
仿微信悬浮窗,可直接协议加入悬浮窗或者直接调用方法注册,可自定义转场动画 演示 myFloat.gif 用法1 在Appdelegate中注册 传入对应控制器的className //只带控制器的cl ...
- android 仿ios悬浮窗,iOS高仿微信悬浮窗、忍者小猪游戏、音乐播放器、支付宝、今日头条布局滚动效果等源码...
iOS精选源码 iOS优质博客 之前看了很多面试题,感觉要不是不够就是过于冗余,于是我将网上的一些面试题进行了删减和重排,现在分享给大家.(题目来源于网络,侵删)1. Object-c的类可以多重继承 ...
- iOS高仿微信悬浮窗、忍者小猪游戏、音乐播放器、支付宝、今日头条布局滚动效果等源码...
iOS精选源码 iOS WKWebView的使用源码 模仿apple music 小播放器的交互实现 高仿微信的悬浮小窗口 iOS仿支付宝首页效果 [swift]仿微信悬浮窗 类似于今日头条,网易新闻 ...
- 【Android】利用WindowsManager悬浮窗播放本地视频以及下载线上视频保存在本地
基于目前又重新拾起了分屏的项目需求,对之前研究的分屏播放视频做了更深入的研究.在之前的基础上做了改进和用户优化上的处理,实现了原生的VideoView加载本地视频.并使用FileDownLoader下 ...
- Android安卓仿IOS音量调节-自定义view系列(4)
Android安卓仿IOS音量调节-自定义view系列 功能简介 主要实现步骤 xml相关属性设置 java代码 Android技术生活交流 更多其他页面-自定义View-实用功能合集:点击查看 Gi ...
- Android高仿iOS Messages聊天气泡
Android高仿iOS Messages聊天气泡 一.目标 二.功能分析 三.实现代码 1. ChatItem 2. DateItem 3. TextItem 4. PhotoItem 5. Cha ...
- Android高仿iOS Messages录音操作按钮
Android高仿iOS Messages录音操作按钮 目录 一.目标 二.功能分析 三.实现效果 四.实现过程 五.开发过程回顾 六.接下来 七.Finally 前面的2次开发,分别完成了实现录音和 ...
- Android 摄像头预览悬浮窗,可拖动,可显示在其他app上方
市面上常见的摄像头悬浮窗,如微信.手机QQ的视频通话功能,有如下特点: 整屏页面能切换到一个小的悬浮窗 悬浮窗能运行在其他app上方 悬浮窗能跳回整屏页面,并且悬浮窗消失 我们探讨过用CameraX打 ...
- android浮标权限管理,Android辅助权限与悬浮窗
Android辅助权限与悬浮窗在执行自动化服务的流程中,我们其实并不希望被用户的操作中断流程,所以有什么方法在用户点击自动化操作的过程中,避免用户再次操作呢?那就是开启一个全局透明的悬浮窗,进行屏蔽触 ...
最新文章
- 修改系统UIAlertAction的按钮颜色
- xilinx FPGA的远程更新(动态加载)详解(Using a Microprocessor to Configure 7 Series FPGAs)
- 全志A33-linux内核early_printk分析及使用
- html,css颜色,色系
- php元类,什么是元类-python编程入门系列图文教程-PHP中文网教程
- css3 微信聊天小尖角,用CSS制作聊天框小尖角、气泡效果
- 利用python实现杜利特尔分解法
- Dragonfly软件电脑环境
- error LNK2005解决方法
- 常用计算方法(C语言代码)(计算方法课程)
- iOS 将http%3A%2F%2F解析为URL
- 启动优化·基础论·浅析 Android 启动优化
- html记不住单词,只需这4种方法,让你30分钟记住100单词
- 5阶无向完全图_第8章 几种典型图(5}.ppt
- 数字证书常见格式整理
- bcd码和16进制的区别
- 2021年三个季度的监控摄像头品牌排名
- 干货!面向人群计数的跨模态协作表征学习方法和大规模RBGT数据集
- hdu_5151_Sit sit sit(区间DP)
- 解决qtcreator无法启动