一 窗口过渡动画

1.1 案例效果图

效果图

1.2 案例源码

1.2.1 添加权限 (AndroidManifest.xml)

<!-- 系统悬浮窗权限(Android 6.0+需动态请求) -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

1.2.2 窗口显示动画 (win_anim_enter.xml)

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"><scaleandroid:duration="1000"android:fromXScale="0.2"android:fromYScale="0.2"android:toXScale="1"android:toYScale="1" /><alphaandroid:duration="1000"android:fromAlpha="0.2"android:toAlpha="1" />
</set>

1.2.3 窗口移除动画 (win_anim_exit.xml)

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"><scaleandroid:duration="1000"android:fromXScale="0.2"android:fromYScale="0.2"android:toXScale="1"android:toYScale="1" /><alphaandroid:duration="1000"android:fromAlpha="0.2"android:toAlpha="1" />
</set>

1.2.4 动画样式 (themes.xml)

<?xml version="1.0" encoding="utf-8"?>
<resources><style name="floatAnimStyle"><item name="android:windowEnterAnimation">@anim/win_anim_enter</item><item name="android:windowExitAnimation">@anim/win_anim_exit</item></style>
</resources>

1.2.5 布局文件 (activity_main.xml)

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:gravity="center"><Buttonandroid:id="@+id/btnAdd"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="添加窗口"/><Buttonandroid:id="@+id/btnRemove"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="移除窗口"/>
</LinearLayout>

1.2.6 悬浮窗布局 (float_layout.xml)

<TextView xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/floatingView"android:layout_width="200dp"android:layout_height="100dp"android:background="#77000000"android:text="悬浮窗口"android:textColor="#FF0000"android:gravity="center"/>

1.2.7 MainActivity 实现

public class MainActivity extends AppCompatActivity {private static final int REQUEST_OVERLAY_PERMISSION = 100;private WindowManager mWindowManager;private View mFloatView;private WindowManager.LayoutParams layoutParams;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 初始化mWindowManagermWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);findViewById(R.id.btnAdd).setOnClickListener(v -> addFloatingWindow());findViewById(R.id.btnRemove).setOnClickListener(v -> removeFloatingWindow());}private void addFloatingWindow() {// 检查悬浮窗权限if (!Settings.canDrawOverlays(this)) {requestOverlayPermission();return;}if (mFloatView != null) return;  // 防止重复添加// 创建悬浮窗视图mFloatView = LayoutInflater.from(this).inflate(R.layout.float_layout, null);mFloatView.setOnTouchListener(new ViewTouchListener());// 设置窗口参数int type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;layoutParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, type, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);// 初始位置layoutParams.gravity = Gravity.TOP | Gravity.START;layoutParams.x = 100;layoutParams.y = 300;layoutParams.windowAnimations = R.style.floatAnimStyle; //窗口过渡动画try {mWindowManager.addView(mFloatView, layoutParams);} catch (Exception e) {Toast.makeText(this, "添加失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();}}private void removeFloatingWindow() {if (mFloatView != null) {try {mWindowManager.removeView(mFloatView);mFloatView = null;} catch (Exception e) {Toast.makeText(this, "移除失败", Toast.LENGTH_SHORT).show();}}}// 处理拖拽的触摸监听器private class ViewTouchListener implements View.OnTouchListener {private int initialX, initialY;private float initialTouchX, initialTouchY;@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:initialX = layoutParams.x;initialY = layoutParams.y;initialTouchX = event.getRawX();initialTouchY = event.getRawY();return true;case MotionEvent.ACTION_MOVE:layoutParams.x = initialX + (int) (event.getRawX() - initialTouchX);layoutParams.y = initialY + (int) (event.getRawY() - initialTouchY);mWindowManager.updateViewLayout(mFloatView, layoutParams);return true;}return false;}}// 请求悬浮窗权限private void requestOverlayPermission() {Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));startActivityForResult(intent, REQUEST_OVERLAY_PERMISSION);}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == REQUEST_OVERLAY_PERMISSION) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(this)) {addFloatingWindow();}}}@Overrideprotected void onDestroy() {removeFloatingWindow(); // 防止Activity销毁后窗口残留super.onDestroy();}
}

二 过渡动画层级图分析

2.1 使用winscope抓取SurfaceFlinger图层信息

抓取图层信息。

adb shell su root service call SurfaceFlinger 1025 i32 1 #开始录制
adb shell su root service call SurfaceFlinger 1025 i32 0 #停止录制
adb pull /data/misc/wmtrace . //获取捕获的SurfaceFlinger图层信

使用adb指令将抓取的SurfaceFlinger图层信息导出来。
SurfaceFlinger图层信息

2.2 使用winscope进行分析

2.2.1 窗口未被添加到WMS中

01
层级树的叶子节点Leaf:3:14#0还看不到我们的窗口节点。

2.2.2 窗口被添加到WMS中

02
可以在层级树的叶子节点Leaf:3:14#0看到新加的Sys2038窗口图层信息。

2.2.3 窗口显示过渡动画开始

03

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"><scaleandroid:duration="1000"android:fromXScale="0.2"android:fromYScale="0.2"android:toXScale="1"android:toYScale="1" /><alphaandroid:duration="1000"android:fromAlpha="0.2"android:toAlpha="1" />
</set>

多了一个含有animation-leash关键字的Surface图层,bounds接近我们在动画样式文件win_anim_enter.xml里面设置的初始值0.2倍,透明度同样接近我们设置的初始值0.2。

2.2.4 窗口显示过渡动画即将结束

04
bound接近1.0,透明度也接近1.0,过渡动画即将执行完毕。

2.2.5 窗口显示过渡动画结束

05
animation-leash关键字的Surface图层被移除,只剩下我们要添加的类型为Sys2038的窗口图层。

2.2.6 窗口准备被移除,开始执行过渡动画

06

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"><scaleandroid:duration="1000"android:fromXScale="1"android:fromYScale="1"android:toXScale="0.2"android:toYScale="0.2" /><alphaandroid:duration="1000"android:fromAlpha="1"android:toAlpha="0.2" /></set>

多了一个含有animation-leash关键字的Surface图层,bounds是我们在动画样式文件win_anim_exit.xml里面设置的初始值1.0倍,透明度同样是我们设置的初始值1.0。

2.2.7 窗口被移除过渡动画即将结束

07
bound接近0.2,透明度也接近0.2,过渡动画即将执行完毕。

2.2.8 窗口被移除过渡动画结束

08
animation-leash关键字的Surface图层被移除,类型为Sys2038的之前添加的窗口图层也被移除,层级树的叶子节点Leaf:3:14#0恢复到了原来的样式。

2.3 窗口过渡动画期间的图层信息变化

过渡动画

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/91291.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/91291.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/91291.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

腾讯云WAF域名分级防护实战笔记

基于业务风险等级、合规要求及腾讯云最佳实践&#xff0c;提供可直接落地的配置方案&#xff0c;供学习借鉴&#xff1a;一、域名分级与防护原则1. ​域名分级清单&#xff08;核心资产&#xff09;​​​主域名​​业务类型​​风险等级​​合规要求​​防护等级​example.com…

1. 请说出你知道的水平垂直居中的方法

总结 容器 flex 布局&#xff0c;jsutify-content: center; align-items: center;容器 flex 布局&#xff0c;子项 margin: auto;容器 relative 布局&#xff0c;子项 absolute 布局&#xff0c;left: 50%; top: 50%; transform: translate(-50%, -50%);子项 absolute 布局&…

VS Code `launch.json` 完整配置指南:参数详解 + 配置实例

文章目录&#x1f4e6; 一、基本结构&#x1f50d; 二、单个配置项详解示例配置&#xff1a;&#x1f9e9; 三、字段说明与可选值&#x1f4c1; 四、常用变量&#xff08;宏替换&#xff09;&#x1f6e0;️ 五、常见配置实例1️⃣ 调试当前打开的 .py 文件2️⃣ 调试 Jupyter …

使用浏览器inspect调试wx小程序

edge://inspect/#devices调试wx小程序 背景&#xff1a; 在开发混合项目的过程中&#xff0c;常常需要在app环境排查问题&#xff0c;接口可以使用fiddler等工具来抓包&#xff0c;但是js错误就不好抓包了&#xff0c;这里介绍一种调试工具-浏览器。 调试过程 首先电脑打开edg…

【论文阅读】-《Simple Black-box Adversarial Attacks》

简单黑盒对抗攻击 Chuan Guo Jacob R. Gardner Yurong You Andrew Gordon Wilson Kilian Q. Weinberger 摘要 我们提出了一种在黑盒&#xff08;black-box&#xff09;场景下构建对抗样本&#xff08;adversarial images&#xff09;的极其简单的方法。与白盒&#xff08;…

基于ASP.NET+SQL Server实现(Web)企业进销存管理系统

企业进销存管理系统的设计和实现一、摘要进销存管理是现代企业生产经营中的重要环节&#xff0c;是完成企业资源配置的重要管理工作&#xff0c;对企业生产经营效率的最大化发挥着重要作用。本文以我国中小企业的进销存管理为研究对象&#xff0c;描述了企业进销存管理系统从需…

(LeetCode 面试经典 150 题 ) 15. 三数之和 (排序+双指针)

题目&#xff1a;15. 三数之和 思路&#xff1a;排序双指针&#xff0c;时间复杂度0(n^2nlogn)。 先将数组nums升序排序&#xff0c;方便去重和使用双指针。第一层for循环来枚举第一位数&#xff0c;后面使用双指针来找到第二个、第三个数即可&#xff0c;细节看注释。 C版本…

easy-springdoc

介绍 简化springdoc的使用&#xff08;可以搭配knife4j-openapi3-jakarta-spring-boot-starter一起使用&#xff09; maven引用 <dependency><groupId>io.github.xiaoyudeguang</groupId><artifactId>easy-springdoc</artifactId><version>…

配置nodejs,若依

1.配置node.js环境 Node.js — Download Node.js 1.下载好一路下一步&#xff0c;可以安装到d盘 装完之后执行 npm -v 显示版本号即安装成功 2.安装好后新建两个文件夹&#xff0c;node_cache和node_global 3.配置环境变量 新建变量 在path里编辑变量 4.配置用户变量 5.…

Python学习之路(十二)-开发和优化处理大数据量接口

文章目录一、接口设计原则二、性能优化策略1. 数据库优化2. 缓存机制3. 并发模型三、内存管理技巧1. 内存优化实践2. 避免内存泄漏四、接口测试与监控1. 性能测试2. 日志与监控3. 错误处理与限流五、代码示例&#xff08;Flask 流式处理&#xff09;六、部署建议一、接口设计原…

【实时Linux实战系列】实时数据流的网络传输

在实时系统中&#xff0c;数据流的实时传输是许多应用场景的核心需求之一。无论是工业自动化中的传感器数据、金融交易中的高频数据&#xff0c;还是多媒体应用中的视频流&#xff0c;都需要在严格的时间约束内完成数据的传输。实时数据流的传输不仅要求高吞吐量&#xff0c;还…

C#数组(一维数组、多维数组、交错数组、参数数组)

在 C# 中&#xff0c;数组是一种用于存储固定大小的相同类型元素的集合。数组可以包含值类型、引用类型或对象类型的元素&#xff0c;并且在内存中是连续存储的。以下是关于 C# 数组的详细介绍&#xff1a;1. 一维数组声明与初始化// 声明数组 int[] numbers; // 声…

Dify离线安装包-集成全部插件、模板和依赖组件,方便安可内网使用

项目介绍 Dify一键离线安装包&#xff0c;集成安装了全部插件、模板&#xff0c;并集成了dify全部插件所需的依赖组件。方便你在内网、安可环境等离线状态下使用。 Dify是一个开源的LLM应用开发平台。其直观的界面结合了AI工作流、RAG管道、Agent、模型管理、可观测性功能等&…

面试150 翻转二叉树

思路 采用先序遍历&#xff0c;可以通过新建根节点node&#xff0c;将原来root的右子树连到去node的左子树中&#xff0c;root的左子树连到去node的右子树中。 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): …

C++-linux系统编程 3.gcc编译工具

GCC编译工具链完全指南 GCC&#xff08;GNU Compiler Collection&#xff09;是Linux系统下最常用的编译器套件&#xff0c;支持C、C、Objective-C等多种编程语言。本章将深入讲解GCC的编译流程、常用选项及项目实战技巧。 一、GCC编译的四个核心阶段 GCC编译一个程序需要经过四…

uView UI 组件大全

uView UI 是一个基于 uni-app 的高质量 UI 组件库&#xff0c;提供丰富的跨平台组件&#xff08;支持 H5、小程序、App 等&#xff09;。以下是其核心组件的分类大全及功能说明&#xff0c;结合最新版本&#xff08;1.2.10&#xff09;整理&#xff1a; &#x1f4e6; 一、基础…

QWidget 和 QML 的本质和使用上的区别

QWidget 和 QML 是 Qt 框架中两种不同的 UI 开发技术&#xff0c;它们在底层实现、设计理念和使用场景上有显著区别。以下是它们的本质和主要差异&#xff1a;1. 本质区别特性QWidgetQML (Qt Modeling Language)技术基础基于 C 的面向对象控件库基于声明式语言&#xff08;类似…

中转模型服务的风险

最近发现一些 AI 相关帖子下&#xff0c;存在低质 claude code 中转的小广告。 其中转的基本原理就是 claude code 允许自己提供 API endpoint 和 key&#xff0c;可以使用任意一个 OpenAI API 兼容的供应商&#xff0c;就这么简单。 进一点 claude token&#xff0c;再混入一点…

前端Vue.js面试题(3)

✨✨✨目录 1.v-model的原理是什么样的&#xff1f; 2.Vue的生命周期&#xff1f; 3.Vue子组件和父组件执行顺序&#xff1f; 4.created和mounted的区别&#xff1f; 5.vue中&#xff0c;推荐在哪个生命周期发起请求&#xff1f; 6.keep-alive中的生命周期有哪些&#xf…

leetcode:HJ18 识别有效的IP地址和掩码并进行分类统计[华为机考][字符串]

学习要点 bitset<8>ostringstreamstoistring.findstring.substr 题目链接 识别有效的IP地址和掩码并进行分类统计_牛客题霸_牛客网 题目描述 解法 #include <iostream> #include <bits/stdc.h> #include <sstream> #include <string> #inclu…