Android 16系统源码_窗口动画(一)窗口过渡动画层级图分析

一 窗口过渡动画

1.1 案例效果图

1.2 案例源码

1.2.1 添加权限 (AndroidManifest.xml)

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

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

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

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

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

1.2.4 动画样式 (themes.xml)

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)

xml 复制代码
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <Button
        android:id="@+id/btnAdd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="添加窗口"/>

    <Button
        android:id="@+id/btnRemove"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="移除窗口"/>
</LinearLayout>

1.2.6 悬浮窗布局 (float_layout.xml)

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 实现

java 复制代码
public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_OVERLAY_PERMISSION = 100;
    private WindowManager mWindowManager;
    private View mFloatView;
    private WindowManager.LayoutParams layoutParams;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 初始化mWindowManager
        mWindowManager = (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;

        @Override
        public 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);
    }

    @Override
    protected 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();
            }
        }
    }

    @Override
    protected void onDestroy() {
        removeFloatingWindow(); // 防止Activity销毁后窗口残留
        super.onDestroy();
    }
}

二 过渡动画层级图分析

2.1 使用winscope抓取SurfaceFlinger图层信息

抓取图层信息。

bash 复制代码
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图层信息导出来。

2.2 使用winscope进行分析

2.2.1 窗口未被添加到WMS中

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

2.2.2 窗口被添加到WMS中

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

2.2.3 窗口显示过渡动画开始

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:duration="1000"
        android:fromXScale="0.2"
        android:fromYScale="0.2"
        android:toXScale="1"
        android:toYScale="1" />
    <alpha
        android: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 窗口显示过渡动画即将结束

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

2.2.5 窗口显示过渡动画结束

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

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

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:duration="1000"
        android:fromXScale="1"
        android:fromYScale="1"
        android:toXScale="0.2"
        android:toYScale="0.2" />
    <alpha
        android: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 窗口被移除过渡动画即将结束

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

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

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

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

相关推荐
祖国的好青年1 小时前
VS Code 搭建 React Native 开发环境(Windows 实战指南)
android·windows·react native·react.js
黄林晴1 小时前
警惕!AGP 9.2 别只改版本号,R8 规则与构建链路全线收紧
android·gradle
小米渣的逆袭2 小时前
Android ADB 完全使用指南
android·adb
儿歌八万首2 小时前
Jetpack Compose Canvas 进阶:结合 animateFloatAsState 让自定义图形动起来
android·动画·compose
zhangphil3 小时前
Android Page 3 Flow读sql数据库媒体文件,Kotlin
android·kotlin
神探小白牙3 小时前
echarts,3d堆叠图
android·3d·echarts
李白的天不白3 小时前
如何项目发布到github上
android·vue.js
summerkissyou19873 小时前
Android-RTC、NTP 和 System Time(系统时间)
android
小书房3 小时前
Kotlin使用体验及理解1
android·开发语言·kotlin
撩得Android一次心动4 小时前
Android Navigation 组件全面讲解
android·jetpack·navigation