Android 16系统源码_窗口动画(一)窗口过渡动画层级图分析
一 窗口过渡动画
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图层信息导出来。
2.2 使用winscope进行分析
2.2.1 窗口未被添加到WMS中
层级树的叶子节点Leaf:3:14#0还看不到我们的窗口节点。
2.2.2 窗口被添加到WMS中
可以在层级树的叶子节点Leaf:3:14#0看到新加的Sys2038窗口图层信息。
2.2.3 窗口显示过渡动画开始
<?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 窗口显示过渡动画即将结束
bound接近1.0,透明度也接近1.0,过渡动画即将执行完毕。
2.2.5 窗口显示过渡动画结束
animation-leash关键字的Surface图层被移除,只剩下我们要添加的类型为Sys2038的窗口图层。
2.2.6 窗口准备被移除,开始执行过渡动画
<?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 窗口被移除过渡动画即将结束
bound接近0.2,透明度也接近0.2,过渡动画即将执行完毕。
2.2.8 窗口被移除过渡动画结束
animation-leash关键字的Surface图层被移除,类型为Sys2038的之前添加的窗口图层也被移除,层级树的叶子节点Leaf:3:14#0恢复到了原来的样式。