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 窗口过渡动画期间的图层信息变化

相关推荐
Industio_触觉智能36 分钟前
量产技巧之RK3588 Android12默认移除导航栏&状态栏
android·rk3588·开发板·核心板·瑞芯微·rk3588j
小馬佩德罗38 分钟前
Android系统的问题分析笔记 - Android上的调试方式 bugreport
android·调试
VividnessYao38 分钟前
Android Handler 消息机制
android
iReaShare2 小时前
如何将华为文件传输到电脑
android
火柴就是我2 小时前
每日见闻之Rust中 trait 的孤儿规则
android·rust
IT 前端 张2 小时前
uni-app在安卓设备上获取 (WIFI 【和】以太网) ip 和 MAC
android·tcp/ip·uni-app
iReaShare2 小时前
如何轻松将音乐从安卓设备传输到安卓设备
android
狂浪天涯3 小时前
Android 16 | Display Framework - 2 | Surface
android·操作系统
没有了遇见3 小时前
Android 异常处理机制全解析:虚拟机层、Java 层与 Native 层
android