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

相关推荐
Just_Paranoid1 分钟前
【SystemUI】锁屏来通知默认亮屏Wake模式
android·framework·systemui·keyguard·aod
没有了遇见7 分钟前
Android +,++,+= 的区别
android·kotlin
_无_妄_1 小时前
Android 使用 WebView 直接加载 PDF 文件,通过 JS 实现
android
VomPom2 小时前
手写一个精简版Koin:深入理解依赖注入核心原理
android
IT乐手2 小时前
Java 编写查看调用栈信息
android
Digitally3 小时前
如何轻松永久删除 Android 手机上的短信
android·智能手机
JulyYu3 小时前
Flutter混合栈适配安卓ActivityResult
android·flutter
Warren984 小时前
Appium学习笔记
android·windows·spring boot·笔记·后端·学习·appium
Kapaseker5 小时前
Compose 文本适配天花板?BasicText 自动调大小实战
android·kotlin
海的天空16617 小时前
Flutter旧版本升级-> Android 配置、iOS配置
android·flutter·ios