安卓悬浮窗口, 丝滑双指缩放视频窗口
最重要的事情说前面: demo源码:
https://github.com/5800LDW/ProjectFloatingWindow
前言:
1.跨应用的浮动窗口在网上很多资料, 就不细说了。
2.双指缩放View 也很多资料, 可参考:
https://blog.csdn.net/zxq614/article/details/88873729
正文
下面进入正题, 如何把上述结合起来, 下面在前言2 的ZoomView 上进行改造。
下面帖一下原始ZoomView的代码:
public class ZoomView extends RelativeLayout {// 属性变量private float translationX; // 移动Xprivate float translationY; // 移动Yprivate float scale = 1; // 伸缩比例private float rotation; // 旋转角度// 移动过程中临时变量private float actionX;private float actionY;private float spacing;private float degree;private int moveType; // 0=未选择,1=拖动,2=缩放public ZoomView(Context context) {this(context, null);}public ZoomView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public ZoomView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);setClickable(true);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {getParent().requestDisallowInterceptTouchEvent(true);return super.onInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction() & MotionEvent.ACTION_MASK) {case MotionEvent.ACTION_DOWN:moveType = 1;actionX = event.getRawX();actionY = event.getRawY();break;case MotionEvent.ACTION_POINTER_DOWN:moveType = 2;spacing = getSpacing(event);degree = getDegree(event);break;case MotionEvent.ACTION_MOVE:if (moveType == 1) {translationX = translationX + event.getRawX() - actionX;translationY = translationY + event.getRawY() - actionY;setTranslationX(translationX);setTranslationY(translationY);actionX = event.getRawX();actionY = event.getRawY();} else if (moveType == 2) {scale = scale * getSpacing(event) / spacing;setScaleX(scale);setScaleY(scale);rotation = rotation + getDegree(event) - degree;if (rotation > 360) {rotation = rotation - 360;}if (rotation < -360) {rotation = rotation + 360;}setRotation(rotation);}break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_POINTER_UP:moveType = 0;}return super.onTouchEvent(event);}// 触碰两点间距离private float getSpacing(MotionEvent event) {//通过三角函数得到两点间的距离float x = event.getX(0) - event.getX(1);float y = event.getY(0) - event.getY(1);return (float) Math.sqrt(x * x + y * y);}// 取旋转角度private float getDegree(MotionEvent event) {//得到两个手指间的旋转角度double delta_x = event.getX(0) - event.getX(1);double delta_y = event.getY(0) - event.getY(1);double radians = Math.atan2(delta_y, delta_x);return (float) Math.toDegrees(radians);}}
具体思路
下面具体讲思路, 具体代码在文首:
1.首先
我们不需要旋转, 所以注释掉
//setRotation(rotation);
2.我们要解决平移问题。
上文的setTranslationX是相对于父布局来实现的, 但是我们悬浮窗不会充满全屏, 而且后面我们要缩小/放大, 父布局的大小是要变化的, 所以上文的setTranslationX和translationY都要注释掉。
悬浮窗移动是通过WindowManager的updateViewLayout方法来实现的。所以我们加入一个内部接口, 提供给WindowManager进行调用updateViewLayout, 修改后的代码如下:
ZoomView的修改如下:
case MotionEvent.ACTION_MOVE:if (moveType == 1) {translationX = translationX + event.getRawX() - actionX;;translationY = translationY + event.getRawY() - actionY;//父view先变换位置就行了;if (translationListener != null) {translationListener.translation(translationX, translationY);}//setTranslationX(translationX);//setTranslationY(translationY);actionX = event.getRawX();actionY = event.getRawY();}
接口TranslationListener的translation方法的具体实现:
@Overridepublic void translation(float actionX, float actionY) {wmParams.x = (int) actionX;wmParams.y = (int) actionY;wm.updateViewLayout(view, wmParams);}
3.解决缩放的问题。
ZoomView的setScaleX(scale); setScaleY(scale); 是需要保留的, 同时, 父view也要进行缩放/放大。
所以我们同样引入一个接口方法提供给Params进行修改 width 和 height ,然后调用WindowManager的updateViewLayout。
ZoomView的修改如下:
else if (moveType == 2) {scale = scale * getSpacing(event) / spacing;//核心是先改变父类的位置, 然后再缩小子类if (translationListener != null) {translationListener.scale(scale);}setScaleX(scale);setScaleY(scale);
接口TranslationListener的scale方法的具体实现:
...
@Override
public void scale(float scale) {//因为我把悬浮窗默认的宽是300, 所以这里就直接拿300作为宽高的初始值, 后面的缩放、放大都是在这个初始值上进行float normalPx = dp2px(300f);Log.e("TAG ", " normalPx = " + normalPx);wmParams.width = (int) (normalPx * scale);wmParams.height = (int) (normalPx * scale);wm.updateViewLayout(view, wmParams);}
...
到这一步的时候, 能平移也能缩小/放大了, 但是, 会有个问题, 就是ZoomView不会在父view居中了, 经测试, 使用ConstraintLayout包裹ZoomView, 并设置ZoomView居中, 在缩放ZoomView后能居中在父布局中显示。这里可能跟约束布局的实现有关, 有了解的同学可以说一下...
4.视频播放。
下面我们就放入视频播放的view被ZoomView包裹 , 看看视频播放是否卡顿。
源码里面使用的是GSYVideoPlayer的播放控件。 图就不截图了, 具体看源码就OK。
5.视频播放后引入的问题。
视频播放的控件也是会获取触控事件的, 会导致ZoomView的onTouchEvent不生效, 所以这里涉及到事件分发的问题。但是, ZoomView处理View的位置、大小变化, 本身不跟播放的View的触摸事件有冲突的, 所以我们把ZoomView的onTouchEvent的变化位置、大小的代码改到onInterceptTouchEvent里面实现即可。
题外话: demo是基础实现, 具体很多细节, 例如设定最大的放大数值, 设定最小的缩小数值等等, 还得需要进行处理的。
感谢并推荐阅读:
https://blog.csdn.net/zxq614/article/details/88873729
https://github.com/CarGuo/GSYVideoPlayer
相关文章:
安卓悬浮窗口, 丝滑双指缩放视频窗口
最重要的事情说前面: demo源码:https://github.com/5800LDW/ProjectFloatingWindow前言:1.跨应用的浮动窗口在网上很多资料, 就不细说了。2.双指缩放View 也很多资料, 可参考:https://blog.csdn.net/zxq614/article/details/88873729正文下面进入正题, 如何把上述结合起来, 下面…...

300左右哪款蓝牙耳机适合学生用?四款便宜质量好的蓝牙耳机推荐
近年来,随着蓝牙耳机的发展,不管是音质、外观、佩戴还是降噪都有了很大的提升。但是我们在入手蓝牙耳机时,最好还是根据预算和需求入手。在此,我来给预算在三百内的朋友推荐几款便宜质量好的蓝牙耳机,可以当个参考。 …...

桥梁设计模式
介绍 Java桥梁模式(也称桥接模式)(Bridge Pattern)是一种设计模式,它将抽象和实现分离,使它们可以独立地变化.它通过一个大类或者一系列紧密关联的类拆分成两个独立的层次结构来实现这种分离,其中一个层次结构包含抽象类或接口,另一个层次结构包含实现类.桥梁模式使得抽象类和…...
【华为OD机试 2023最新 】 新员工座位(C++)
文章目录 题目描述输入描述输出描述用例题目解析C++题目描述 工位由序列F1,F2…Fn组成,Fi值为0、1或2。其中0代表空置,1代表有人,2代表障碍物。 1、某一空位的友好度为左右连续老员工数之和, 2、为方便新员工学习求助,优先安排友好度高的空位, 给出工位序列,求所有空…...
蓝桥杯刷题第二十二天
第一题:受伤的皇后题目描述有一个 nn 的国际象棋棋盘(n 行 n 列的方格图),请在棋盘中摆放 n 个受伤的国际象棋皇后,要求:任何两个皇后不在同一行。任何两个皇后不在同一列。如果两个皇后在同一条 45 度角的…...

CentOS从gcc 4.8.5 升级到gcc 8.3.1
gcc -v查看当前gcc版本。 sudo yum install centos-release-scl-rh安装centos-release-scl-rh。 sudo yum install devtoolset-8-build安装devtoolset-8-build。 显示“Complete!”表示安装成功。 sudo yum install devtoolset-8-gdb安装devtoolset-8-gdb。 显示“Comple…...

【人人都能读标准】12. 原始类型的编码形式
本文为《人人都能读标准》—— ECMAScript篇的第12篇。我在这个仓库中系统地介绍了标准的阅读规则以及使用方式,并深入剖析了标准对JavaScript核心原理的描述。 ECMAScript有7种原始类型,分别是Undefined、Null、Boolean、String、Number、BigInt、Symbo…...

VUE进行前后端交互
目录 一、 跨域 1. 什么是跨域? 2. 什么是本域? 3. 浏览器请求的三种报错 二、SpringBoot解决跨域问题其他前后端跨域请求解决方案 1. SpringBoot上直接添加CrossOrigin 2. 处理跨域请求的Configuration 3. 采用过滤器的方式 3.1 方式一 3.2 方式…...
ThingsBoard Gateway:物联网设备数据采集与集成的强大解决方案
文章目录ThingsBoard Gateway:物联网设备数据采集与集成的强大解决方案1\. ThingsBoard Gateway:概述2\. 主要特点与优势3\. 应用场景4\. 如何使用ThingsBoard Gateway:物联网设备数据采集与集成的强大解决方案 随着物联网(IoT&a…...
什么是镜像/raid
镜像(Mirroring)是一种文件存储形式,是冗余的一种类型,一个磁盘上的数据在另一个磁盘上存在一个完全相同的副本即为镜像。可以把许多文件做成一个镜像文件,与GHOST等程序放在一个盘里用GHOST等软件打开后,又…...

【Python】如何有效比较两个时间序列在图形上的相似度?
文章目录前言一、1.准备二、实操1.使用Matplotlib可视化比较两个时间序列2.计算两个时间序列的相关系数:3.使用Python实现动态时间规整算法(DTW):总结前言 比较两个时间序列在图形上是否相似,可以通过以下方法&#x…...

JavaEE-常见的锁策略和synchronized的锁机制
目录常见的锁策略乐观锁和悲观锁轻量级锁和重量级锁自旋锁和挂起等待锁普通互斥锁和读写锁公平锁和非公平锁可重入锁和不可重入锁synchronized的锁机制synchronized特性锁升级/锁膨胀锁消除锁粗化常见的锁策略 乐观锁和悲观锁 乐观锁和悲观锁主要是看主要是锁竞争的激烈程度.…...

信息化,数字化,智能化是三种不同的概念吗?
前两年流行“信息化”,网上铺天盖地都是关于“信息化”的文章,这两年开始流行起“数字化”,于是铺天盖地都是“数字化”的文章。(这一点从数字化和信息化这两个关键词热度趋势就可以看出来)。 但点开那些文章仔细看看…...
【华为OD机试 2023最新 】 匿名信(C++ 100%)
题目描述 电视剧《分界线》里面有一个片段,男主为了向警察透露案件细节,且不暴露自己,于是将报刊上的字减下来,剪拼成匿名信。 现在又一名举报人,希望借鉴这种手段,使用英文报刊完成举报操作。 但为了增加文章的混淆度,只需满足每个单词中字母数量一致即可,不关注每个…...

硬件语言Verilog HDL牛客刷题day05 时序逻辑部分
1.VL29 信号发生器 1.题目: 题目描述: 请编写一个信号发生器模块,根据波形选择信号wave_choise发出相应的波形:wave_choice0时,发出方波信号;wave_choice1时,发出锯齿波信号;wave…...

Ajax 入门
前端技术:在浏览器中执行的程序都是前端技术。如 html、css、js 等 后端技术:在服务器中执行的长须,使用 Java 等语言开发的后端程序。servlet,jsp,jdbc,mysql,tomacat 等 全局刷新 使用表单…...

半导体器件基础06:发光二极管
说在开头:关于玻尔原子模型(1) 卢瑟福的模型面临着与经典电磁波理论的矛盾,按照经典电磁波理论,卢瑟福的原子不可能稳定存在超过1秒钟。玻尔面临着选择:要么放弃卢瑟福模型,要么放弃麦克斯韦伟…...

AutoCV第二课:Python基础
目录Python基础前言1.流程控制1.1 条件语句1.2 循环语句1.2.1 while循环语句1.2.2 for循环语句1.3 作业1.4 拓展-try except语法2.函数2.1 函数定义2.2 函数的参数2.2.1 位置参数2.2.2 命名参数2.2.3 默认参数2.2.4 可变参数2.2.5 参数展开2.3 递归函数2.3.1 递归函数定义2.3.2…...
LeetCode算法 打家劫舍 和 打家劫舍II C++
目录题目 打家劫舍参考答案题目 打家劫舍II参考答案题目 打家劫舍 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯…...

蓝桥杯刷题冲刺 | 倒计时10天
作者:指针不指南吗 专栏:蓝桥杯倒计时冲刺 🐾马上就要蓝桥杯了,最后的这几天尤为重要,不可懈怠哦🐾 文章目录1.有边数限制的最短路2.九进制转十进制1.有边数限制的最短路 题目 链接: 853. 有边数…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...

Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...

【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...