精华内容
下载资源
问答
  • windows过渡动画
    2017-09-21 09:54:00
    原文: 背水一战 Windows 10 (17) - 动画: ThemeTransition(过渡效果)

    [源码下载]


    背水一战 Windows 10 (17) - 动画: ThemeTransition(过渡效果)



    作者:webabcd


    介绍
    背水一战 Windows 10 之 动画

    • ThemeTransition 的概述
    • EntranceThemeTransition - 页面间跳转时的过渡效果
    • ContentThemeTransition - 内容改变时的过渡效果
    • RepositionThemeTransition - 位置改变时的过渡效果
    • PopupThemeTransition - 弹出时的过渡效果
    • AddDeleteThemeTransition - 添加项或删除项时的过渡效果
    • ReorderThemeTransition - 对集合中的元素重新排序时的过渡效果
    • PaneThemeTransition - 基于边缘的较大 UI 滑入和滑出时的过渡效果
    • EdgeUIThemeTransition - 基于边缘的较小 UI 滑入和滑出时的过渡



    示例
    1、过渡效果的概述
    Animation/ThemeTransition/Summary.xaml

    <Page
        x:Class="Windows10.Animation.ThemeTransition.Summary"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Windows10.Animation.ThemeTransition"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="Transparent">
            <StackPanel Margin="10 0 10 10">
    
                <TextBlock Text="请参见本 xaml 中的注释" />
    
                <!--
                    UIElement.Transitions - 指定 UIElement 的过渡效果
                
                    <Rectangle>
                        <Rectangle.Transitions>
                            <TransitionCollection>
                                <EntranceThemeTransition/>
                            </TransitionCollection>
                        </Rectangle.Transitions>
                    </Rectangle>
                -->
    
    
                <!--
                    Panel.ChildrenTransitions - 指定 Panel 的子元素们的过渡效果
                
                    <WrapGrid>
                        <WrapGrid.ChildrenTransitions>
                            <TransitionCollection>
                                <EntranceThemeTransition/>
                            </TransitionCollection>
                        </WrapGrid.ChildrenTransitions>
                    </WrapGrid>
                -->
    
    
                <!--
                    ItemsControl.ItemContainerTransitions - 指定 ItemsControl 的项容器的过渡效果
                
                    <ItemsControl>
                        <ItemsControl.ItemContainerTransitions>
                            <TransitionCollection>
                                <EntranceThemeTransition/>
                            </TransitionCollection>
                        </ItemsControl.ItemContainerTransitions>
                    </ItemsControl>
                -->
    
    
                <!--
                    ContentControl.ContentTransitions - 指定 ContentControl 的过渡效果
                
                    <ContentControl>
                        <ContentControl.ContentTransitions>
                            <TransitionCollection>
                                <EntranceThemeTransition/>
                            </TransitionCollection>
                        </ContentControl.ContentTransitions>
                    </ContentControl>
                -->
    
            </StackPanel>
        </Grid>
    </Page>


    2、演示 EntranceThemeTransition
    Animation/ThemeTransition/Entrance.xaml

    <Page
        x:Class="Windows10.Animation.ThemeTransition.Entrance"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Windows10.Animation.ThemeTransition"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="Transparent">
            <StackPanel Margin="10 0 10 10">
    
                <!--
                    EntranceThemeTransition - 页面间跳转时的过渡效果
                        FromHorizontalOffset - 初始位置的水平偏移量
                        FromVerticalOffset - 初始位置的垂直偏移量
                        IsStaggeringEnabled - 当包含多个子元素时,是否需要错开呈现它们
                -->
                <Frame Name="frame" Width="400" Height="100" HorizontalAlignment="Left" VerticalAlignment="Top">
                    <Frame.ContentTransitions>
                        <TransitionCollection>
                            <EntranceThemeTransition IsStaggeringEnabled="False" />
                        </TransitionCollection>
                    </Frame.ContentTransitions>
                </Frame>
    
                <Button Name="btnGotoFrame1" Content="导航至 Frame1" Click="btnGotoFrame1_Click" Margin="0 10 0 0" />
                <Button Name="btnGotoFrame2" Content="导航至 Frame2" Click="btnGotoFrame2_Click" Margin="0 10 0 0" />
    
                <ItemsControl x:Name="itemsControl" Margin="0 10 0 0">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <WrapGrid>
                                <WrapGrid.ChildrenTransitions>
                                    <TransitionCollection>
                                        <EntranceThemeTransition IsStaggeringEnabled="True" />
                                    </TransitionCollection>
                                </WrapGrid.ChildrenTransitions>
                            </WrapGrid>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.Items>
                        <Rectangle Width="100" Height="100" Fill="Red" />
                        <Rectangle Width="100" Height="100" Fill="Green" />
                        <Rectangle Width="100" Height="100" Fill="Blue" />
                    </ItemsControl.Items>
                    <ItemsControl.Template>
                        <ControlTemplate>
                            <Border BorderBrush="Orange" BorderThickness="1">
                                <ItemsPresenter Margin="10" VerticalAlignment="Center" HorizontalAlignment="Center" />
                            </Border>
                        </ControlTemplate>
                    </ItemsControl.Template>
                </ItemsControl>
    
            </StackPanel>
        </Grid>
    </Page>

    Animation/ThemeTransition/Entrance.xaml.cs

    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    
    namespace Windows10.Animation.ThemeTransition
    {
        public sealed partial class Entrance : Page
        {
            public Entrance()
            {
                this.InitializeComponent();
            }
    
            private void btnGotoFrame1_Click(object sender, RoutedEventArgs e)
            {
                frame.Navigate(typeof(Frame1));
            }
    
            private void btnGotoFrame2_Click(object sender, RoutedEventArgs e)
            {
                frame.Navigate(typeof(Frame2));
            }
        }
    }

    Animation/ThemeTransition/Frame1.xaml

    <Page
        x:Class="Windows10.Animation.ThemeTransition.Frame1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Windows10.Animation.ThemeTransition"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="Transparent">
            <StackPanel Margin="10 0 10 10">
                <TextBlock Name="lblMsg" Text="我是 Frame1" />
            </StackPanel>
        </Grid>
    </Page>

    Animation/ThemeTransition/Frame2.xaml

    <Page
        x:Class="Windows10.Animation.ThemeTransition.Frame2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Windows10.Animation.ThemeTransition"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="Transparent">
            <StackPanel Margin="10 0 10 10">
                <TextBlock Name="lblMsg" Text="我是 Frame2" />
            </StackPanel>
        </Grid>
    </Page>


    3、演示 ContentThemeTransition
    Animation/ThemeTransition/Content.xaml

    <Page
        x:Class="Windows10.Animation.ThemeTransition.Content"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Windows10.Animation.ThemeTransition"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="Transparent">
            <StackPanel Margin="10 0 10 10">
    
                <!--
                    ContentThemeTransition - 内容改变时的过渡效果
                        FromHorizontalOffset - 初始位置的水平偏移量
                        FromVerticalOffset - 初始位置的垂直偏移量
                -->
                <ContentControl Name="contentControl" PointerPressed="contentControl_PointerPressed">
                    <ContentControl.ContentTransitions>
                        <TransitionCollection>
                            <ContentThemeTransition />
                        </TransitionCollection>
                    </ContentControl.ContentTransitions>
                    <ContentControl.Content>
                        <Rectangle Height="200" Width="200" Fill="Orange" />
                    </ContentControl.Content>
                </ContentControl>
    
    
                <!--
                    如果要在 ScrollViewer 或其他继承了 ContentControl 的控件中应用 ContentThemeTransition 的话,应该用如下方式
                -->
                <ScrollViewer Name="scrollViewer" Margin="0 10 0 0" PointerPressed="scrollViewer_PointerPressed">
                    <ContentControl Content="{Binding}">
                        <ContentControl.ContentTemplate>
                            <DataTemplate>
                                <StackPanel Orientation="Vertical">
                                    <Rectangle Height="200" Width="200" Fill="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
                                </StackPanel>
                            </DataTemplate>
                        </ContentControl.ContentTemplate>
                        <ContentControl.ContentTransitions>
                            <TransitionCollection>
                                <ContentThemeTransition/>
                            </TransitionCollection>
                        </ContentControl.ContentTransitions>
                    </ContentControl>
                </ScrollViewer>
    
            </StackPanel>
        </Grid>
    </Page>

    Animation/ThemeTransition/Content.xaml.cs

    using System;
    using Windows.UI;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Shapes;
    
    namespace Windows10.Animation.ThemeTransition
    {
        public sealed partial class Content : Page
        {
            public Content()
            {
                this.InitializeComponent();
            }
    
            // 改变 ContentControl 的内容
            private void contentControl_PointerPressed(object sender, PointerRoutedEventArgs e)
            {
                Rectangle rectangle = new Rectangle();
                Random random = new Random();
    
                rectangle.Height = 200;
                rectangle.Width = 200;
                rectangle.Fill = new SolidColorBrush(Color.FromArgb(255, (byte)random.Next(0, 255), (byte)random.Next(0, 255), (byte)random.Next(0, 255)));
    
                contentControl.Content = rectangle;
            }
    
            // 绑定最新的数据到 ScrollViewer
            private void scrollViewer_PointerPressed(object sender, PointerRoutedEventArgs e)
            {
                Random random = new Random();
                scrollViewer.DataContext = new SolidColorBrush(Color.FromArgb(255, (byte)random.Next(0, 255), (byte)random.Next(0, 255), (byte)random.Next(0, 255)));
            }
        }
    }


    4、演示 RepositionThemeTransition
    Animation/ThemeTransition/Reposition.xaml

    <Page
        x:Class="Windows10.Animation.ThemeTransition.Reposition"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Windows10.Animation.ThemeTransition"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="Transparent">
            <StackPanel Margin="10 0 10 10">
    
                <Button Name="btnMove" Content="移动 rectangle" Click="btnMove_Click" Margin="0 0 0 10" />
    
                <!--
                    RepositionThemeTransition - 位置改变时的过渡效果
                -->
                <Rectangle Name="rectangle" Width="400" Height="100" Fill="Orange" HorizontalAlignment="Left">
                    <Rectangle.Transitions>
                        <TransitionCollection>
                            <RepositionThemeTransition />
                        </TransitionCollection>
                    </Rectangle.Transitions>
                </Rectangle>
    
            </StackPanel>
        </Grid>
    </Page>

    Animation/ThemeTransition/Reposition.xaml.cs

    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    
    namespace Windows10.Animation.ThemeTransition
    {
        public sealed partial class Reposition : Page
        {
            public Reposition()
            {
                this.InitializeComponent();
            }
    
            // 改变矩形的位置
            private void btnMove_Click(object sender, RoutedEventArgs e)
            {
                if (rectangle.Margin == new Thickness(0))
                    rectangle.Margin = new Thickness(100);
                else
                    rectangle.Margin = new Thickness(0);
            }
        }
    }


    5、演示 PopupThemeTransition
    Animation/ThemeTransition/Popup.xaml

    <Page
        x:Class="Windows10.Animation.ThemeTransition.Popup"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Windows10.Animation.ThemeTransition"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="Transparent">
            <StackPanel Margin="10 0 10 10">
    
                <!--
                    PopupThemeTransition - 弹出时的过渡效果
                        FromHorizontalOffset - 初始位置的水平偏移量
                        FromVerticalOffset - 初始位置的垂直偏移量
                -->
                <Popup Name="popup" HorizontalOffset="200" VerticalOffset="10" IsLightDismissEnabled="True">
                    <Popup.Child>
                        <Border BorderBrush="Red" BorderThickness="1" Background="Blue" Width="200" Height="200">
                            <TextBlock Text="我是 Popup" HorizontalAlignment="Center" />
                        </Border>
                    </Popup.Child>
                    <Popup.ChildTransitions>
                        <TransitionCollection>
                            <PopupThemeTransition />
                        </TransitionCollection>
                    </Popup.ChildTransitions>
                </Popup>
    
                <Button Name="btnPopup" Content="弹出 Popup" Click="btnPopup_Click" Margin="0 10 0 0" />
    
            </StackPanel>
        </Grid>
    </Page>

    Animation/ThemeTransition/Popup.xaml.cs

    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    
    namespace Windows10.Animation.ThemeTransition
    {
        public sealed partial class Popup : Page
        {
            public Popup()
            {
                this.InitializeComponent();
            }
    
            // 显示 Popup
            private void btnPopup_Click(object sender, RoutedEventArgs e)
            {
                if (!popup.IsOpen)
                    popup.IsOpen = true;
            }
        }
    }


    6、演示 AddDeleteThemeTransition
    Animation/ThemeTransition/AddDelete.xaml

    <Page
        x:Class="Windows10.Animation.ThemeTransition.AddDelete"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Windows10.Animation.ThemeTransition"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="Transparent">
            <StackPanel Margin="10 0 10 10">
    
                <Button x:Name="btnAddItem" Content="Add Item" Click="btnAddItem_Click"/>
                <Button x:Name="btnDeleteItem" Content="Delete Item" Click="btnDeleteItem_Click" Margin="0 10 0 0" />
    
                <!--
                    AddDeleteThemeTransition - 添加项或删除项时的过渡效果
                -->
                <ItemsControl x:Name="itemsControl" Margin="0 10 0 0">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <WrapGrid>
                                <WrapGrid.ChildrenTransitions>
                                    <TransitionCollection>
                                        <AddDeleteThemeTransition />
                                    </TransitionCollection>
                                </WrapGrid.ChildrenTransitions>
                            </WrapGrid>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.Items>
                        <Rectangle Width="100" Height="100" Fill="Red" />
                        <Rectangle Width="100" Height="100" Fill="Green" />
                        <Rectangle Width="100" Height="100" Fill="Blue" />
                    </ItemsControl.Items>
                    <ItemsControl.Template>
                        <ControlTemplate>
                            <Border BorderBrush="Orange" BorderThickness="1">
                                <ItemsPresenter Margin="10" VerticalAlignment="Center" HorizontalAlignment="Center" />
                            </Border>
                        </ControlTemplate>
                    </ItemsControl.Template>
                </ItemsControl>
    
            </StackPanel>
        </Grid>
    </Page>

    Animation/ThemeTransition/AddDelete.xaml.cs

    using System;
    using Windows.UI;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Shapes;
    
    namespace Windows10.Animation.ThemeTransition
    {
        public sealed partial class AddDelete : Page
        {
            public AddDelete()
            {
                this.InitializeComponent();
            }
    
            // 添加项
            private void btnAddItem_Click(object sender, RoutedEventArgs e)
            {
                Rectangle rectangle = new Rectangle();
                Random random = new Random();
    
                rectangle.Height = 100;
                rectangle.Width = 100;
                rectangle.Fill = new SolidColorBrush(Color.FromArgb(255, (byte)random.Next(0, 255), (byte)random.Next(0, 255), (byte)random.Next(0, 255)));
    
                itemsControl.Items.Add(rectangle);
            }
    
            // 删除项
            private void btnDeleteItem_Click(object sender, RoutedEventArgs e)
            {
                if (itemsControl.Items.Count > 0)
                    itemsControl.Items.RemoveAt(itemsControl.Items.Count - 1);
            }
        }
    }


    7、演示 ReorderThemeTransition
    Animation/ThemeTransition/Reorder.xaml

    <Page
        x:Class="Windows10.Animation.ThemeTransition.Reorder"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Windows10.Animation.ThemeTransition"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="Transparent">
            <StackPanel Margin="10 0 10 10">
    
                <Button x:Name="btnAddItem" Content="Add Item" Click="btnAddItem_Click" />
    
                <!--
                    ReorderThemeTransition - 对集合中的元素重新排序时的过渡效果
                -->
                <ItemsControl x:Name="itemsControl" Margin="0 10 0 0">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <WrapGrid>
                                <WrapGrid.ChildrenTransitions>
                                    <TransitionCollection>
                                        <ReorderThemeTransition />
                                    </TransitionCollection>
                                </WrapGrid.ChildrenTransitions>
                            </WrapGrid>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.Items>
                        <Rectangle Width="100" Height="100" Fill="Red" />
                        <Rectangle Width="100" Height="100" Fill="Green" />
                        <Rectangle Width="100" Height="100" Fill="Blue" />
                    </ItemsControl.Items>
                    <ItemsControl.Template>
                        <ControlTemplate>
                            <Border BorderBrush="Orange" BorderThickness="1">
                                <ItemsPresenter Margin="10" VerticalAlignment="Center" HorizontalAlignment="Center" />
                            </Border>
                        </ControlTemplate>
                    </ItemsControl.Template>
                </ItemsControl>
    
            </StackPanel>
        </Grid>
    </Page>

    Animation/ThemeTransition/Reorder.xaml.cs

    using System;
    using Windows.UI;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Shapes;
    
    namespace Windows10.Animation.ThemeTransition
    {
        public sealed partial class Reorder : Page
        {
            public Reorder()
            {
                this.InitializeComponent();
            }
    
            // 在集合的位置 2 处添加新的元素,以达到重新排序的效果
            private void btnAddItem_Click(object sender, RoutedEventArgs e)
            {
                Rectangle rectangle = new Rectangle();
                Random random = new Random();
    
                rectangle.Height = 100;
                rectangle.Width = 100;
                rectangle.Fill = new SolidColorBrush(Color.FromArgb(255, (byte)random.Next(0, 255), (byte)random.Next(0, 255), (byte)random.Next(0, 255)));
    
                itemsControl.Items.Insert(2, rectangle);
            }
        }
    }


    8、演示 PaneThemeTransition
    Animation/ThemeTransition/Pane.xaml

    <Page
        x:Class="Windows10.Animation.ThemeTransition.Pane"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Windows10.Animation.ThemeTransition"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="Transparent">
            <StackPanel Margin="10 0 10 10">
    
                <!--
                    PaneThemeTransition - 基于边缘的较大 UI 滑入和滑出时的过渡效果
                        Edge - 边缘(Left, Top, Right, Bottom)
                -->
                <Popup Name="popup" HorizontalOffset="0" VerticalOffset="50" IsLightDismissEnabled="True">
                    <Popup.Child>
                        <Border BorderBrush="Red" BorderThickness="1" Background="Blue" Width="800" Height="200">
                            <TextBlock Text="我是 Popup" HorizontalAlignment="Center" />
                        </Border>
                    </Popup.Child>
                    <Popup.ChildTransitions>
                        <TransitionCollection>
                            <PaneThemeTransition Edge="Top" />
                        </TransitionCollection>
                    </Popup.ChildTransitions>
                </Popup>
    
                <Button Name="btnShowPane" Content="显示 Pane" Click="btnShowPane_Click" Margin="0 10 0 0" />
    
            </StackPanel>
        </Grid>
    </Page>

    Animation/ThemeTransition/Pane.xaml.cs

    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    
    namespace Windows10.Animation.ThemeTransition
    {
        public sealed partial class Pane : Page
        {
            public Pane()
            {
                this.InitializeComponent();
            }
    
            // 显示 Pane
            private void btnShowPane_Click(object sender, RoutedEventArgs e)
            {
                if (!popup.IsOpen)
                    popup.IsOpen = true;
            }
        }
    }


    9、演示 EdgeUIThemeTransition
    Animation/ThemeTransition/EdgeUI.xaml

    <Page
        x:Class="Windows10.Animation.ThemeTransition.EdgeUI"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Windows10.Animation.ThemeTransition"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
        
        <Grid Background="Transparent">
            <StackPanel Margin="10 0 10 10">
    
                <!--
                    EdgeUIThemeTransition - 基于边缘的较小 UI 滑入和滑出时的过渡效果
                        Edge - 边缘(Left, Top, Right, Bottom)
                -->
                <Popup Name="popup" HorizontalOffset="0" VerticalOffset="50" IsLightDismissEnabled="True">
                    <Popup.Child>
                        <Border BorderBrush="Red" BorderThickness="1" Background="Blue" Width="200" Height="50">
                            <TextBlock Text="我是 Popup" HorizontalAlignment="Center" />
                        </Border>
                    </Popup.Child>
                    <Popup.ChildTransitions>
                        <TransitionCollection>
                            <EdgeUIThemeTransition Edge="Top" />
                        </TransitionCollection>
                    </Popup.ChildTransitions>
                </Popup>
    
                <Button Name="btnShowEdgeUI" Content="显示 EdgeUI" Click="btnShowEdgeUI_Click" Margin="0 10 0 0" />
    
            </StackPanel>
        </Grid>
    </Page>

    Animation/ThemeTransition/EdgeUI.xaml.cs

    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    
    namespace Windows10.Animation.ThemeTransition
    {
        public sealed partial class EdgeUI : Page
        {
            public EdgeUI()
            {
                this.InitializeComponent();
            }
    
            // 显示 EdgeUI
            private void btnShowEdgeUI_Click(object sender, RoutedEventArgs e)
            {
                if (!popup.IsOpen)
                    popup.IsOpen = true;
            }
        }
    }



    OK
    [源码下载]

    更多相关内容
  • Windows Store App 过渡动画 在开发Windows应用商店应用程序时,如果希望界面元素进入或者离开屏幕时显得自然和流畅,可以为其添加过渡动画过渡动画能够及时地提示用户屏幕所发生的变化,不会让用户觉得元素的位置...

    Windows Store App 过渡动画

     

     

    在开发Windows应用商店应用程序时,如果希望界面元素进入或者离开屏幕时显得自然和流畅,可以为其添加过渡动画。过渡动画能够及时地提示用户屏幕所发生的变化,不会让用户觉得元素的位置变化过于突然。下面通过一个示例介绍如何为界面中的文本块添加过渡动画。

    在一个打开的Windows应用商店项目中新建一个空白页,并命名为TransitionAnimationPage,双击打开此页面的TransitionAnimationPage.xaml文件,在Grid元素中添加如下代码。

    <TextBlock Text="过渡动画" FontSize="50">

         <TextBlock.Transitions>

             <TransitionCollection>

                 <EntranceThemeTransition/>

             </TransitionCollection>

         </TextBlock.Transitions>

    </TextBlock>

    在上面的代码中,添加了一个用于实现过渡动画的文本块,通过Text属性将其本文内容设置为“过渡动画”,并使用FontSize属性设置字体的大小为50像素。接着在TextBlock.Transitions元素中添加一个TransitionCollection元素作为文本块的过渡动画集合,并在其中添加了一个EntranceThemeTransition动画,此动画可以使TextBlock文本块在进入屏幕时产生位置过渡的动画效果。

    运行此页面,可以看到添加了过渡动画的文本块会在水平方向上从右向左划入屏幕。动画效果如图10-2所示。

    clip_image002[4]

    10-2 过渡动画效果图1

    从图10-2中可以看到,动画开始时,文本块会沿着箭头的指向滑入屏幕,经过短暂的时间后会停在屏幕中的某一位置,而具体停在什么位置可以根据文本块的Margin属性值来确定,在本示例中并没有设置文本块的Margin属性值,因此文本块会默认停在屏幕的左上角。除此之外,读者需要注意的是,如果为最外层的Grid元素添加过渡动画,那么其中的所有子元素都会有过渡动画效果,读者可以根据实际开发中具体情况选择为恰当的元素添加过渡动画。

    前面介绍的EntranceThemeTransition动画对象中包含了可以控制动画效果的FromHorizontalOffsetFromVerticalOffset属性,通过指定这两个属性的值可以控制界面元素的起始位置相对于目标位置在水平和竖直方向上的偏移量,使添加过渡动画的界面元素以预想的轨迹进入屏幕。

    下面再次回到之前添加的TransitionAnimationPage页面,双击打开TransitionAnimationPage.xaml文件,指定文本块在屏幕上的滑动距离,代码如下所示:

    <TextBlock Text="过渡动画" FontSize="50">

         <TextBlock.Transitions>

             <TransitionCollection>

                <EntranceThemeTransition FromHorizontalOffset="500" FromVerticalOffset="500"/>

             </TransitionCollection>

         </TextBlock.Transitions>

    </TextBlock>

    以上代码在原来的基础上又将EntranceThemeTransition动画对象的FromHorizontalOffsetFromVerticalOffset属性值分别设置为500像素,以指定文本块在水平方向与竖直方向的滑动距离。

    运行此页面,屏幕上产生的过渡动画效果如图10-3所示。

    clip_image004[4]

    10-3过渡动画效果图2

    从图10-3中可以看到,动画开始时文本块会沿着箭头的指向滑入屏幕,这与前一个过渡动画示例相比,文本块的位置变化显得更加醒目。

    至此已经介绍了如何为单一的TextBlock文本块添加过渡动画,除此之外,还可以为对象容器添加过渡动画,这样在对象容器中的所有子对象将会产生更加有趣的动画效果。下面通过一个示例讲解如何为对象容器添加过渡动画效果。

    在一个打开的Windows应用商店项目中新建一个空白页,并命名为EntranceThemeTransitionPage,双击打开此页面的EntranceThemeTransitionPage.xaml文件,在原有的Grid元素中添加如下代码。

    <ItemsControl>

        <ItemsControl.Items>

           <Ellipse Fill= "Yellow" Width="100" Height="100" Margin="10"/>

           <Ellipse Fill=" Yellow" Width="100" Height="100" Margin="10"/>

           <Ellipse Fill=" Yellow" Width="100" Height="100" Margin="10"/>

           <Ellipse Fill=" Yellow" Width="100" Height="100" Margin="10"/>

        </ItemsControl.Items>

        <ItemsControl.ItemContainerTransitions>

            <TransitionCollection>

                <EntranceThemeTransition FromHorizontalOffset="100" FromVerticalOffset="200"/>

            </TransitionCollection>

        </ItemsControl.ItemContainerTransitions>

        <ItemsControl.ItemsPanel>

            <ItemsPanelTemplate>

                <WrapGrid Height="300"/>

            </ItemsPanelTemplate>

        </ItemsControl.ItemsPanel>

    </ItemsControl>

    在上面的代码中,添加了一个ItemsControl元素,此元素作为对象容器,通过ItemsControl.Items元素向其中添加了4个宽和高分别为100像素的Ellipse元素。接着又在对象容器的ItemsControl.ItemContainerTransitions元素中添加一个TransitionCollection元素作为过渡动画集合,并在其中添加了一个EntranceThemeTransition动画对象,这样对象容器中的所有Ellipse元素就都可以产生过渡动画效果。又将EntranceThemeTransition动画对象的FromHorizontalOffsetFromVerticalOffset属性值分别设置为100200像素,以调节对象容器中每个Ellipse元素的移动方向和距离。最后在ItemsControl.ItemsPanel元素中添加一个ItemsPanelTemplate元素,并在ItemsPanelTemplate元素中添加一个WrapGrid元素,通过此元素的Height属性设置容器对象中所有子对象在竖直方向上的布局范围为300像素。

    运行此页面,可以看到对象容器中的椭圆会一个接一个的显示在屏幕中。动画效果如图10-4所示。

    clip_image006[4]

    10-4 将过渡动画用于多个控件上的动画效果图

    上面的示例只是实现了椭圆进入屏幕的开场动画效果,如果希望对象容器中的子对象位置发生更改时会产生重新调整位置的动画效果,需要将RepositionThemeTransition动画应用到包含多个子对象的对象容器中。下面的示例为对象容器中的每个矩形都添加了RepositionThemeTransition动画,若删除容器中的一个矩形,其他的矩形将重新调整到新的合理位置。

    在一个打开的Windows 应用商店项目中新建一个空白页,并命名为RepositionThemeTransitionPage,双击打开此页面的RepositionThemeTransitionPage.xaml文件,在Grid元素中添加如下代码。

    <ItemsControl x:Name ="MyItemsControl">

         <ItemsControl.Items>

            <Rectangle Fill="Red" Width="100" Height="100" Margin="10"/>

            <Rectangle Fill="Red" Width="100" Height="100" Margin="10"/>

            <Rectangle Fill="Red" Width="100" Height="100" Margin="10"/>

            <Rectangle Fill="Red" Width="100" Height="100" Margin="10"/>

        </ItemsControl.Items>

        <ItemsControl.ItemContainerTransitions>

            <TransitionCollection>

                <!--如果没有这个部分,当有项被删除时不会有动画效果-->

                <RepositionThemeTransition/>

            </TransitionCollection>

        </ItemsControl.ItemContainerTransitions>

        <ItemsControl.ItemsPanel>

            <ItemsPanelTemplate>

                <WrapGrid Height="300"/>

            </ItemsPanelTemplate>

        </ItemsControl.ItemsPanel>

    </ItemsControl>

    <Button Content="删除矩形" Click="RemoveButton_Click" Margin="50,200,0,0"/>

    上述代码的实现与之前讲解的示例代码类似,同样是需要添加一个对象容器,不同的是,本示例在后台代码中调用了RemoveAt方法对容器中的子对象执行了删除操作,因此将代表对象容器的ItemsControl元素命名为MyItemsControl,这样即可通过此名称调用RemoveAt方法来删除容器中的一个Rectangle元素。接着在ItemsControl.Items元素中添加4个宽高分别为100像素的Rectangle元素,并在此容器的ItemsControl.ItemContainerTransitions元素中添加一个TransitionCollection元素作为过渡动画集合,其中添加了一个RepositionThemeTransition动画对象,这样对象容器中的每个Rectangle元素都可以产生重新调整位置的过渡动画效果。然后在ItemsControl.ItemsPanel元素中添加一个ItemsPanelTemplate元素,在ItemsPanelTemplate元素中添加一个WrapGrid元素,通过此元素的Height属性设置容器对象中所有子对象在竖直方向的活动范围为300像素。最后在界面上添加一个按钮,并为其Click事件定义一个名为“RemoveButton_Click”的事件处理方法,以实现删除容器中矩形的操作。

    接下来在后台代码中为“删除矩形”按钮添加事件处理方法RemoveButton_Click,当单击此按钮时,会删除对象容器中的一个矩形,代码如下所示:

    private void RemoveButton_Click(object sender, RoutedEventArgs e)

    {

        if (MyItemsControl.Items.Count > 0)

        {        

            MyItemsControl.Items.RemoveAt(0);

        }                                      

    }

    方法通过if语句判断对象容器中是否存在矩形,如果存在,那么每单击一次按钮就会删除一个矩形,与此同时对象容器中剩余的矩形将会产生重新调整位置的动画效果。

    除了上面介绍的过渡动画以外,还有其他类型的过渡动画。在进行界面元素的添加、删除和重新排列等操作时,也可以使用下面的过渡动画。

    q  AddDeleteThemeTransition,在控件中的子对象或内容增减时提供过渡动画。

    q  ContentThemeTransition在控件内容被更改的情况下提供过渡动画。

    q  ReorderThemeTransition,在列表形式的控件中,每个列表项通过拖放改变其顺序后产生的过渡动画,不同的控件和主题可能具有不同的动画效果。

    通过前面的示例,也许读者已经注意到了为控件添加过渡动画效果其实就是向过渡动画集合TransitionCollection中加入相应的动画,如果要实现多种过渡动画效果,可以向TransitionCollection动画集合中同时添加多个动画。然而并不是所有的情形都适合使用过渡动画,如果过渡动画效果不能满足应用的需求,可以添加即将在下一节中介绍的演示图板动画。

     

    展开全文
  • 如何为Windows Mobile WTL应用程序上的子视图过渡设置动画
  • 通过合理的设置安卓中过渡动画的缩放速度,可以让安卓手机中窗口的切换更自然、更流畅,操作上感觉更加的丝滑。安卓手机上一般有窗口动画缩放(window_animation_scale)、过渡动画缩放(transition_animation_scale)和...

    前言

    通过合理的设置安卓中过渡动画的缩放速度,可以让安卓手机中窗口的切换更自然、更流畅,操作上感觉更加的丝滑。安卓手机上一般有窗口动画缩放(window_animation_scale)、过渡动画缩放(transition_animation_scale)和Animator时长缩放(animator_duration_scale)可做设置。

    但是大多安卓系统对于动画缩放速度的设置大多只有几个档位,如0.5x、0.75x、1x、1.5x、2x、5x、10x,无法随心所欲的调节,而且档位不多,无论怎么做组合,要不就是感觉动画快了,要不就是慢了,亦或是感觉生硬。本文带来的就是打破这个局限,随性DIY,找到那个最适合你的。

    开始

    首先本文是通过adb的方式改变安卓手机系统的动画缩放速度,而且脚本中adb的连接方式为USB连接。

    步骤:

    1. 手机USB连接电脑
    2. 打开你安卓手机的开发者选项(如已开启进入下一步;如何开启,可以百度搜索自己手机怎么开启开发者选项)
    3. 进入开发者选择,打开USB调试,允许使用USB调试,手机可能会提示授权电脑允许USB调试,请点确认
    4. 下载文件SetAnimation.zip,解压,双击SetAnimation文件夹下的SetAnimation.bat脚本运行
    5. 脚本会连接手机,手机可能会提示授权电脑允许USB调试,请点确认
    6. 按照脚本的菜单设置即可,一般实测设置1.2-1.4会感觉比较丝滑,脚本限制了动画速度只能在0-10

    检查是否设置成功:

    • 脚本在菜单页面会获取并显示你手机目前的缩放速度
    • 设置后检查开发者选项里动画缩放,原来为1x,设置为1.25x,若从1x变为1.5x则设置成功
    • 可以设置一个大一点的值,如5,可以明显感觉到动画过渡得很慢。

    注意:设置完把USB调试给关了!!!

    脚本界面:
    SetAnimation

    附录

    代码附录,Windows的bat脚本,依赖于adb,照猫画虎写的,没啥技术含量,方便大家设置安卓的动画缩放速度。如果你有adb环境,可以保存成bat文件在Windows直接运行。

    @REM @Author: Myles
    @REM @Date:   2020-03-04 23:20:52
    
    @echo off
    title 安卓动画缩放设置
    
    
    :checkConnected
    echo.&echo 正在连接安卓设备...
    for /f "delims=" %%i in ('adb devices') do (set status=%%i)
    echo %status%|findstr "attached unauthorized" && (goto failToConnect) || (goto menu)
    
     
    :failToConnect
    cls
    echo 连接手机失败,请检查USB调试是否打开和允许!
    echo.&echo 按任意键重试...
    set /p pass=
    goto checkConnected
    
    :menu
    cls
    echo.&echo   提示:
    echo.&echo   建议设置在1.2x-1.4x之间,较为丝滑,多设置试试效果
    echo.&echo   设置后检查开发者选项里动画缩放,原来为1x,设置为1.25x,若从1x变为1.5x则设置成功
    echo.
    echo.&echo   您手机目前的动画缩放值分别为:
    for /f "delims=" %%i in ('adb shell settings get global window_animation_scale') do (set cur_was=%%i)
    for /f "delims=" %%i in ('adb shell settings get global transition_animation_scale') do (set cur_tas=%%i)
    for /f "delims=" %%i in ('adb shell settings get global animator_duration_scale') do ( set cur_ads=%%i)
    echo.&echo   窗口动画缩放:%cur_was%x   过渡动画缩放:%cur_tas%x   Animator时长缩放:%cur_ads%x
    echo.
    echo.&echo   请选择您要执行的操作:
    echo.&echo  【1】默认设置(全部设为1.25x)
    echo.&echo  【2】一次设置(全部设为一个值)
    echo.&echo  【3】逐项设置(一项项设置)
    echo.&echo  【4】退出
    echo.
    
    set /p sel=请输入数字后回车:
    If "%sel%"=="1" (goto setAnimation)
    If "%sel%"=="2" (goto setAnimationAtOnce)
    If "%sel%"=="3" (goto setAnimationOneByOne)
    If "%sel%"=="4" (goto exit)
    echo.&echo 输入无效,请重新输入!
    pause
    goto menu
    
    
    :setAnimation
    :: 窗口动画缩放
    adb shell settings put global window_animation_scale 1.25
    :: 过渡动画缩放
    adb shell settings put global transition_animation_scale 1.25
    :: Animator时长缩放
    adb shell settings put global animator_duration_scale 1.25
    
    echo.&echo 完成,请检查过渡动画是否设置成功!
    echo.&pause
    goto menu
    
    
    :setAnimationAtOnce
    echo.
    set /p val=请输入0-10的缩放值:
    call:verifyInput %val%, setAnimationAtOnce
    :: 窗口动画缩放
    adb shell settings put global window_animation_scale %val%
    :: 过渡动画缩放
    adb shell settings put global transition_animation_scale %val%
    :: Animator时长缩放
    adb shell settings put global animator_duration_scale %val%
    
    echo.&echo 完成,请检查过渡动画是否设置成功!
    echo.&pause
    goto menu
    
    
    :setAnimationOneByOne
    echo.
    
    :: 窗口动画缩放
    :was
    echo.&set /p was=请输入0-10的窗口动画缩放值(window_animation_scale):
    call:verifyInput %was%, was
    adb shell settings put global window_animation_scale %was%
    
    :: 过渡动画缩放
    :tas
    echo.&set /p tas=请输入0-10的过渡动画缩放值(transition_animation_scale):
    call:verifyInput %tas%, tas
    adb shell settings put global transition_animation_scale %tas%
    
    :: Animator时长缩放
    :ads
    echo.&set /p ads=请输入0-10的过渡动画缩放值(animator_duration_scale):
    call:verifyInput %ads%, ads
    adb shell settings put global animator_duration_scale %ads%
    
    echo.&echo 完成,请检查过渡动画是否设置成功!
    echo.&pause
    goto menu
    
    
    @REM 检验输入值(val, goto)
    :verifyInput
    if %~1 lss 0 (
    	echo.&echo 非法输入,请输入0-10的缩放值
    	goto %~2
    )
    if %~1 gtr 10 (
    	echo.&echo 非法输入,请输入0-10的缩放值
    	goto %~2
    )
    goto:eof
    
    :: 退出
    :exit
    taskkill /f /im adb.exe
    exit
    
    展开全文
  • 过渡动画 - 安卓R

    千次阅读 2021-08-02 14:14:15
    本文介绍安卓动画系统中的过渡动画流程。 过渡动画简介 一般过渡动画有在system_server进程中执行的本地动画和在例如systemui等进程执行的远程动画两种情况。 常见的本地动画:App内切换Activity等。 常见的远程动画...

    本文介绍安卓动画系统中的过渡动画流程。

    过渡动画简介

    一般过渡动画有在system_server进程中执行的本地动画和在例如systemui等进程执行的远程动画两种情况。

    常见的本地动画:App内切换Activity等。

    常见的远程动画:从桌面进入App,从App回到桌面,从Notification悬浮窗进入Activity等。

    本地动画

    窗口布局流程中RootWindowContainer的performSurfacePlacement方法开始:

        void performSurfacePlacement() {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");
            try {
                performSurfacePlacementNoTrace();
            } finally {
                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            }
        }
    
        // "Something has changed!  Let's make it correct now."
        // TODO: Super crazy long method that should be broken down...
        void performSurfacePlacementNoTrace() {
            if (DEBUG_WINDOW_TRACE) Slog.v(TAG, "performSurfacePlacementInner: entry. Called by "
                    + Debug.getCallers(3));
    
            int i;
    
            if (mWmService.mFocusMayChange) {
                mWmService.mFocusMayChange = false;
                mWmService.updateFocusedWindowLocked(
                        UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
            }
    
            // Initialize state of exiting tokens.
            final int numDisplays = mChildren.size();
            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
                final DisplayContent displayContent = mChildren.get(displayNdx);
                displayContent.setExitingTokensHasVisible(false);
            }
    
            mHoldScreen = null;
            mScreenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
            mUserActivityTimeout = -1;
            mObscureApplicationContentOnSecondaryDisplays = false;
            mSustainedPerformanceModeCurrent = false;
            mWmService.mTransactionSequence++;
    
            // TODO(multi-display): recents animation & wallpaper need support multi-display.
            final DisplayContent defaultDisplay = mWmService.getDefaultDisplayContentLocked();
            final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
    
            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                    ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
            mWmService.openSurfaceTransaction();
            try {
                applySurfaceChangesTransaction();
            } catch (RuntimeException e) {
                Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
            } finally {
                mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                        "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
            }
            mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();
    
            checkAppTransitionReady(surfacePlacer);
    
            // Defer starting the recents animation until the wallpaper has drawn
            final RecentsAnimationController recentsAnimationController =
                    mWmService.getRecentsAnimationController();
            if (recentsAnimationController != null) {
                recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
            }
    
            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
                final DisplayContent displayContent = mChildren.get(displayNdx);
                if (displayContent.mWallpaperMayChange) {
                    if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change!  Adjusting");
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                    if (DEBUG_LAYOUT_REPEATS) {
                        surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
                                displayContent.pendingLayoutChanges);
                    }
                }
            }
    
            if (mWmService.mFocusMayChange) {
                mWmService.mFocusMayChange = false;
                mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
                        false /*updateInputWindows*/);
            }
    
            if (isLayoutNeeded()) {
                defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
                if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("mLayoutNeeded",
                        defaultDisplay.pendingLayoutChanges);
            }
    
            handleResizingWindows();
    
            if (mWmService.mDisplayFrozen) {
                ProtoLog.v(WM_DEBUG_ORIENTATION,
                        "With display frozen, orientationChangeComplete=%b",
                        mOrientationChangeComplete);
            }
            if (mOrientationChangeComplete) {
                if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
                    mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
                    mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
                    mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
                }
                mWmService.stopFreezingDisplayLocked();
            }
    
            // Destroy the surface of any windows that are no longer visible.
            i = mWmService.mDestroySurface.size();
            if (i > 0) {
                do {
                    i--;
                    WindowState win = mWmService.mDestroySurface.get(i);
                    win.mDestroying = false;
                    final DisplayContent displayContent = win.getDisplayContent();
                    if (displayContent.mInputMethodWindow == win) {
                        displayContent.setInputMethodWindowLocked(null);
                    }
                    if (displayContent.mWallpaperController.isWallpaperTarget(win)) {
                        displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                    }
                    win.destroySurfaceUnchecked();
                    win.mWinAnimator.destroyPreservedSurfaceLocked();
                } while (i > 0);
                mWmService.mDestroySurface.clear();
            }
    
            // Time to remove any exiting tokens?
            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
                final DisplayContent displayContent = mChildren.get(displayNdx);
                displayContent.removeExistingTokensIfPossible();
            }
    
            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
                final DisplayContent displayContent = mChildren.get(displayNdx);
                if (displayContent.pendingLayoutChanges != 0) {
                    displayContent.setLayoutNeeded();
                }
            }
    
            mWmService.setHoldScreenLocked(mHoldScreen);
            if (!mWmService.mDisplayFrozen) {
                final float brightnessOverride = mScreenBrightnessOverride < PowerManager.BRIGHTNESS_MIN
                        || mScreenBrightnessOverride > PowerManager.BRIGHTNESS_MAX
                        ? PowerManager.BRIGHTNESS_INVALID_FLOAT : mScreenBrightnessOverride;
                int brightnessFloatAsIntBits = Float.floatToIntBits(brightnessOverride);
                // Post these on a handler such that we don't call into power manager service while
                // holding the window manager lock to avoid lock contention with power manager lock.
                mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, brightnessFloatAsIntBits,
                        0).sendToTarget();
                mHandler.obtainMessage(SET_USER_ACTIVITY_TIMEOUT, mUserActivityTimeout).sendToTarget();
            }
    
            if (mSustainedPerformanceModeCurrent != mSustainedPerformanceModeEnabled) {
                mSustainedPerformanceModeEnabled = mSustainedPerformanceModeCurrent;
                mWmService.mPowerManagerInternal.powerHint(
                        PowerHint.SUSTAINED_PERFORMANCE,
                        (mSustainedPerformanceModeEnabled ? 1 : 0));
            }
    
            if (mUpdateRotation) {
                ProtoLog.d(WM_DEBUG_ORIENTATION, "Performing post-rotate rotation");
                mUpdateRotation = updateRotationUnchecked();
            }
    
            if (!mWmService.mWaitingForDrawnCallbacks.isEmpty()
                    || (mOrientationChangeComplete && !isLayoutNeeded()
                    && !mUpdateRotation)) {
                mWmService.checkDrawnWindowsLocked();
            }
    
            final int N = mWmService.mPendingRemove.size();
            if (N > 0) {
                if (mWmService.mPendingRemoveTmp.length < N) {
                    mWmService.mPendingRemoveTmp = new WindowState[N + 10];
                }
                mWmService.mPendingRemove.toArray(mWmService.mPendingRemoveTmp);
                mWmService.mPendingRemove.clear();
                ArrayList<DisplayContent> displayList = new ArrayList();
                for (i = 0; i < N; i++) {
                    final WindowState w = mWmService.mPendingRemoveTmp[i];
                    w.removeImmediately();
                    final DisplayContent displayContent = w.getDisplayContent();
                    if (displayContent != null && !displayList.contains(displayContent)) {
                        displayList.add(displayContent);
                    }
                }
    
                for (int j = displayList.size() - 1; j >= 0; --j) {
                    final DisplayContent dc = displayList.get(j);
                    dc.assignWindowLayers(true /*setLayoutNeeded*/);
                }
            }
    
            forAllDisplays(dc -> {
                dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
                dc.updateSystemGestureExclusion();
                dc.updateTouchExcludeRegion();
            });
    
            // Check to see if we are now in a state where the screen should
            // be enabled, because the window obscured flags have changed.
            mWmService.enableScreenIfNeededLocked();
    
            mWmService.scheduleAnimationLocked();
    
            // Send any pending task-info changes that were queued-up during a layout deferment
            mWmService.mAtmService.mTaskOrganizerController.dispatchPendingTaskInfoChanges();
    
            if (DEBUG_WINDOW_TRACE) Slog.e(TAG, "performSurfacePlacementInner exit");
        }
    

    调用了RootWindowContainer的checkAppTransitionReady方法:

        private void checkAppTransitionReady(WindowSurfacePlacer surfacePlacer) {
            // Trace all displays app transition by Z-order for pending layout change.
            for (int i = mChildren.size() - 1; i >= 0; --i) {
                final DisplayContent curDisplay = mChildren.get(i);
    
                // If we are ready to perform an app transition, check through all of the app tokens
                // to be shown and see if they are ready to go.
                if (curDisplay.mAppTransition.isReady()) {
                    // handleAppTransitionReady may modify curDisplay.pendingLayoutChanges.
                    curDisplay.mAppTransitionController.handleAppTransitionReady();
                    if (DEBUG_LAYOUT_REPEATS) {
                        surfacePlacer.debugLayoutRepeats("after handleAppTransitionReady",
                                curDisplay.pendingLayoutChanges);
                    }
                }
    
                if (curDisplay.mAppTransition.isRunning() && !curDisplay.isAppTransitioning()) {
                    // We have finished the animation of an app transition. To do this, we have
                    // delayed a lot of operations like showing and hiding apps, moving apps in
                    // Z-order, etc.
                    // The app token list reflects the correct Z-order, but the window list may now
                    // be out of sync with it. So here we will just rebuild the entire app window
                    // list. Fun!
                    curDisplay.handleAnimatingStoppedAndTransition();
                    if (DEBUG_LAYOUT_REPEATS) {
                        surfacePlacer.debugLayoutRepeats("after handleAnimStopAndXitionLock",
                                curDisplay.pendingLayoutChanges);
                    }
                }
            }
        }
    

    调用了AppTransitionController的handleAppTransitionReady方法:

        /**
         * Handle application transition for given display.
         */
        void handleAppTransitionReady() {
            mTempTransitionReasons.clear();
            if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons)
                    || !transitionGoodToGo(mDisplayContent.mChangingContainers,
                            mTempTransitionReasons)) {
                return;
            }
            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
    
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO");
            final AppTransition appTransition = mDisplayContent.mAppTransition;
            int transit = appTransition.getAppTransition();
            if (mDisplayContent.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {
                transit = WindowManager.TRANSIT_UNSET;
            }
            mDisplayContent.mSkipAppTransitionAnimation = false;
            mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear();
    
            appTransition.removeAppTransitionTimeoutCallbacks();
    
            mDisplayContent.mWallpaperMayChange = false;
    
            int appCount = mDisplayContent.mOpeningApps.size();
            for (int i = 0; i < appCount; ++i) {
                // Clearing the mAnimatingExit flag before entering animation. It's set to true if app
                // window is removed, or window relayout to invisible. This also affects window
                // visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the
                // transition selection depends on wallpaper target visibility.
                mDisplayContent.mOpeningApps.valueAtUnchecked(i).clearAnimatingFlags();
            }
            appCount = mDisplayContent.mChangingContainers.size();
            for (int i = 0; i < appCount; ++i) {
                // Clearing for same reason as above.
                final ActivityRecord activity = getAppFromContainer(
                        mDisplayContent.mChangingContainers.valueAtUnchecked(i));
                if (activity != null) {
                    activity.clearAnimatingFlags();
                }
            }
    
            // Adjust wallpaper before we pull the lower/upper target, since pending changes
            // (like the clearAnimatingFlags() above) might affect wallpaper target result.
            // Or, the opening app window should be a wallpaper target.
            mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
                    mDisplayContent.mOpeningApps);
    
            // Determine if closing and opening app token sets are wallpaper targets, in which case
            // special animations are needed.
            final boolean hasWallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget() != null;
            final boolean openingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mOpeningApps)
                    && hasWallpaperTarget;
            final boolean closingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mClosingApps)
                    && hasWallpaperTarget;
    
            transit = maybeUpdateTransitToTranslucentAnim(transit);
            transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper,
                    closingAppHasWallpaper);
    
            // Find the layout params of the top-most application window in the tokens, which is
            // what will control the animation theme. If all closing windows are obscured, then there is
            // no need to do an animation. This is the case, for example, when this transition is being
            // done behind a dream window.
            final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
                    mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers);
            final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes);
            final ActivityRecord topOpeningApp =
                    getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */);
            final ActivityRecord topClosingApp =
                    getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */);
            final ActivityRecord topChangingApp =
                    getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
            final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
            overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
    
            final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps)
                    || containsVoiceInteraction(mDisplayContent.mOpeningApps);
    
            final int layoutRedo;
            mService.mSurfaceAnimationRunner.deferStartingAnimations();
            try {
                applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit,
                        animLp, voiceInteraction);
                handleClosingApps();
                handleOpeningApps();
                handleChangingApps(transit);
    
                appTransition.setLastAppTransition(transit, topOpeningApp,
                        topClosingApp, topChangingApp);
    
                final int flags = appTransition.getTransitFlags();
                layoutRedo = appTransition.goodToGo(transit, topOpeningApp,
                        mDisplayContent.mOpeningApps);
                handleNonAppWindowsInTransition(transit, flags);
                appTransition.postAnimationCallback();
                appTransition.clear();
            } finally {
                mService.mSurfaceAnimationRunner.continueStartingAnimations();
            }
    
            mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent);
    
            mDisplayContent.mOpeningApps.clear();
            mDisplayContent.mClosingApps.clear();
            mDisplayContent.mChangingContainers.clear();
            mDisplayContent.mUnknownAppVisibilityController.clear();
    
            // This has changed the visibility of windows, so perform
            // a new layout to get them all up-to-date.
            mDisplayContent.setLayoutNeeded();
    
            mDisplayContent.computeImeTarget(true /* updateImeTarget */);
    
            mService.mAtmService.mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
                    mTempTransitionReasons);
    
            if (transit == TRANSIT_SHOW_SINGLE_TASK_DISPLAY) {
                mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
                    mService.mAtmInternal.notifySingleTaskDisplayDrawn(mDisplayContent.getDisplayId());
                });
            }
    
            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
    
            mDisplayContent.pendingLayoutChanges |=
                    layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;
        }
    

    其中调用了AppTransitionController的applyAnimations方法:

        /**
         * Apply an app transition animation based on a set of {@link ActivityRecord}
         *
         * @param openingApps The list of opening apps to which an app transition animation applies.
         * @param closingApps The list of closing apps to which an app transition animation applies.
         * @param transit The current transition type.
         * @param animLp Layout parameters in which an app transition animation runs.
         * @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice
         *                         interaction session driving task.
         */
        private void applyAnimations(ArraySet<ActivityRecord> openingApps,
                ArraySet<ActivityRecord> closingApps, @TransitionType int transit,
                LayoutParams animLp, boolean voiceInteraction) {
            if (transit == WindowManager.TRANSIT_UNSET
                    || (openingApps.isEmpty() && closingApps.isEmpty())) {
                return;
            }
    
            final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
                    openingApps, closingApps, true /* visible */);
            final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
                    openingApps, closingApps, false /* visible */);
            applyAnimations(openingWcs, openingApps, transit, true /* visible */, animLp,
                    voiceInteraction);
            applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
                    voiceInteraction);
    
            final AccessibilityController accessibilityController =
                    mDisplayContent.mWmService.mAccessibilityController;
            if (accessibilityController != null) {
                accessibilityController.onAppWindowTransitionLocked(
                        mDisplayContent.getDisplayId(), transit);
            }
        }
    
        /**
         * Apply animation to the set of window containers.
         *
         * @param wcs The list of {@link WindowContainer}s to which an app transition animation applies.
         * @param apps The list of {@link ActivityRecord}s being transitioning.
         * @param transit The current transition type.
         * @param visible {@code true} if the apps becomes visible, {@code false} if the apps becomes
         *                invisible.
         * @param animLp Layout parameters in which an app transition animation runs.
         * @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice
         *                         interaction session driving task.
         */
        private void applyAnimations(ArraySet<WindowContainer> wcs, ArraySet<ActivityRecord> apps,
                @TransitionType int transit, boolean visible, LayoutParams animLp,
                boolean voiceInteraction) {
            final int wcsCount = wcs.size();
            for (int i = 0; i < wcsCount; i++) {
                final WindowContainer wc = wcs.valueAt(i);
                // If app transition animation target is promoted to higher level, SurfaceAnimator
                // triggers WC#onAnimationFinished only on the promoted target. So we need to take care
                // of triggering AR#onAnimationFinished on each ActivityRecord which is a part of the
                // app transition.
                final ArrayList<ActivityRecord> transitioningDescendants = new ArrayList<>();
                for (int j = 0; j < apps.size(); ++j) {
                    final ActivityRecord app = apps.valueAt(j);
                    if (app.isDescendantOf(wc)) {
                        transitioningDescendants.add(app);
                    }
                }
                wc.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants);
            }
        }
    

    调用了WindowContainer的applyAnimation方法:

        /**
         * Applies the app transition animation according the given the layout properties in the
         * window hierarchy.
         *
         * @param lp The layout parameters of the window.
         * @param transit The app transition type indicates what kind of transition to be applied.
         * @param enter Whether the app transition is entering transition or not.
         * @param isVoiceInteraction Whether the container is participating in voice interaction or not.
         * @param sources {@link ActivityRecord}s which causes this app transition animation.
         *
         * @return {@code true} when the container applied the app transition, {@code false} if the
         *         app transition is disabled or skipped.
         *
         * @see #getAnimationAdapter
         */
        boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
                boolean isVoiceInteraction, @Nullable ArrayList<WindowContainer> sources) {
            if (mWmService.mDisableTransitionAnimation) {
                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                        "applyAnimation: transition animation is disabled or skipped. "
                                + "container=%s", this);
                cancelAnimation();
                return false;
            }
    
            // Only apply an animation if the display isn't frozen. If it is frozen, there is no reason
            // to animate and it can cause strange artifacts when we unfreeze the display if some
            // different animation is running.
            try {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WC#applyAnimation");
                if (okToAnimate()) {
                    applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
                } else {
                    cancelAnimation();
                }
            } finally {
                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            }
    
            return isAnimating();
        }
    

    调用了WindowContainer的applyAnimationUnchecked方法:

        protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,
                int transit, boolean isVoiceInteraction,
                @Nullable ArrayList<WindowContainer> sources) {
            final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
                    transit, enter, isVoiceInteraction);
            AnimationAdapter adapter = adapters.first;
            AnimationAdapter thumbnailAdapter = adapters.second;
            if (adapter != null) {
                if (sources != null) {
                    mSurfaceAnimationSources.addAll(sources);
                }
                startAnimation(getPendingTransaction(), adapter, !isVisible(),
                        ANIMATION_TYPE_APP_TRANSITION);
                if (adapter.getShowWallpaper()) {
                    getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                }
                if (thumbnailAdapter != null) {
                    mSurfaceFreezer.mSnapshot.startAnimation(getPendingTransaction(),
                            thumbnailAdapter, ANIMATION_TYPE_APP_TRANSITION, (type, anim) -> { });
                }
            }
        }
    

    首先调用了WindowContainer的getAnimationAdapter方法:

        /**
         * Gets the {@link AnimationAdapter} according the given window layout properties in the window
         * hierarchy.
         *
         * @return The return value will always contain two elements, one for normal animations and the
         *         other for thumbnail animation, both can be {@code null}.
         *
         * @See com.android.server.wm.RemoteAnimationController.RemoteAnimationRecord
         * @See LocalAnimationAdapter
         */
        Pair<AnimationAdapter, AnimationAdapter> getAnimationAdapter(WindowManager.LayoutParams lp,
                int transit, boolean enter, boolean isVoiceInteraction) {
            final Pair<AnimationAdapter, AnimationAdapter> resultAdapters;
            ......
            if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) {
                ......
            } else if (isChanging) {
                ......
            } else {
                mNeedsAnimationBoundsLayer = (appStackClipMode == STACK_CLIP_AFTER_ANIM);
                final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
    
                if (a != null) {
                    // Only apply corner radius to animation if we're not in multi window mode.
                    // We don't want rounded corners when in pip or split screen.
                    final float windowCornerRadius = !inMultiWindowMode()
                            ? getDisplayContent().getWindowCornerRadius()
                            : 0;
                    AnimationAdapter adapter = new LocalAnimationAdapter(
                            new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
                                    getDisplayContent().mAppTransition.canSkipFirstFrame(),
                                    appStackClipMode, true /* isAppAnimation */, windowCornerRadius),
                            getSurfaceAnimationRunner());
    
                    resultAdapters = new Pair<>(adapter, null);
                    mNeedsZBoost = a.getZAdjustment() == Animation.ZORDER_TOP
                            || AppTransition.isClosingTransit(transit);
                    mTransit = transit;
                    mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
                } else {
                    resultAdapters = new Pair<>(null, null);
                }
            }
            return resultAdapters;
        }
    

    创建了LocalAnimationAdapter作为AnimationAdapter。

    WindowContainer的applyAnimationUnchecked后面还调用了WindowContainer的startAnimation方法:

        void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
                @AnimationType int type) {
            startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */);
        }
    
        /**
         * Starts an animation on the container.
         *
         * @param anim The animation to run.
         * @param hidden Whether our container is currently hidden. TODO This should use isVisible at
         *               some point but the meaning is too weird to work for all containers.
         * @param type The type of animation defined as {@link AnimationType}.
         * @param animationFinishedCallback The callback being triggered when the animation finishes.
         */
        void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
                @AnimationType int type,
                @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
            if (DEBUG_ANIM) {
                Slog.v(TAG, "Starting animation on " + this + ": type=" + type + ", anim=" + anim);
            }
    
            // TODO: This should use isVisible() but because isVisible has a really weird meaning at
            // the moment this doesn't work for all animatable window containers.
            mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
                    mSurfaceFreezer);
        }
    

    最后在SurfaceAnimator的startAnimation方法中创建Leash并调用Animatable(WindowContainer)的onAnimationLeashCreated方法和AnimationAdapter(LocalAnimationAdapter)的startAnimation方法来开始执行动画。

    远程动画

    以在桌面点击悬浮窗形式的Notification打开Activity为例,整个动画流程是这样的:

    1 在systemui进程中接收点击事件,创建RemoteAnimationAdapter

    从StatusBarNotificationActivityStarter的onNotificationClicked方法开始:

        /**
         * Called when a notification is clicked.
         *
         * @param sbn notification that was clicked
         * @param row row for that notification
         */
        @Override
        public void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row) {
            ......
            ActivityStarter.OnDismissAction postKeyguardAction =
                    () -> handleNotificationClickAfterKeyguardDismissed(
                            sbn, row, controller, intent,
                            isActivityIntent, wasOccluded, showOverLockscreen);
            if (showOverLockscreen) {
                mIsCollapsingToShowActivityOverLockscreen = true;
                postKeyguardAction.onDismiss();
            } else {
                mActivityStarter.dismissKeyguardThenExecute(
                        postKeyguardAction, null /* cancel */, afterKeyguardGone);
            }
        }
    

    在Keyguard消失后会调用StatusBarNotificationActivityStarter的handleNotificationClickAfterKeyguardDismissed方法:

        private boolean handleNotificationClickAfterKeyguardDismissed(
                StatusBarNotification sbn,
                ExpandableNotificationRow row,
                RemoteInputController controller,
                PendingIntent intent,
                boolean isActivityIntent,
                boolean wasOccluded,
                boolean showOverLockscreen) {
            ......
            final Runnable runnable = () -> handleNotificationClickAfterPanelCollapsed(
                    sbn, row, controller, intent,
                    isActivityIntent, wasOccluded, parentToCancelFinal);
    
            if (showOverLockscreen) {
                mShadeController.addPostCollapseAction(runnable);
                mShadeController.collapsePanel(true /* animate */);
            } else if (mKeyguardStateController.isShowing()
                    && mStatusBar.isOccluded()) {
                mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
                mShadeController.collapsePanel();
            } else {
                mBackgroundHandler.postAtFrontOfQueue(runnable);
            }
            return !mNotificationPanel.isFullyCollapsed();
        }
    

    最终会调用StatusBarNotificationActivityStarter的handleNotificationClickAfterPanelCollapsed方法:

        private void handleNotificationClickAfterPanelCollapsed(
                StatusBarNotification sbn,
                ExpandableNotificationRow row,
                RemoteInputController controller,
                PendingIntent intent,
                boolean isActivityIntent,
                boolean wasOccluded,
                NotificationEntry parentToCancelFinal) {
            ......
                startNotificationIntent(
                        intent, fillInIntent, entry, row, wasOccluded, isActivityIntent);
            ......
        }
    

    调用了StatusBarNotificationActivityStarter的startNotificationIntent方法:

        private void startNotificationIntent(
                PendingIntent intent,
                Intent fillInIntent,
                NotificationEntry entry,
                View row,
                boolean wasOccluded,
                boolean isActivityIntent) {
            RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(row,
                    wasOccluded);
            mLogger.logStartNotificationIntent(entry.getKey(), intent);
            try {
                if (adapter != null) {
                    ActivityTaskManager.getService()
                            .registerRemoteAnimationForNextActivityStart(
                                    intent.getCreatorPackage(), adapter);
                }
                int launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
                        null, null, getActivityOptions(adapter));
                mMainThreadHandler.post(() -> {
                    mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent);
                });
            } catch (RemoteException | PendingIntent.CanceledException e) {
                // the stack trace isn't very helpful here.
                // Just log the exception message.
                mLogger.logSendingIntentFailed(e);
                // TODO: Dismiss Keyguard.
            }
        }
    

    调用了ActivityLaunchAnimator的getLaunchAnimation方法:

        public RemoteAnimationAdapter getLaunchAnimation(
                View sourceView, boolean occluded) {
            if (!(sourceView instanceof ExpandableNotificationRow)
                    || !mCallback.areLaunchAnimationsEnabled() || occluded) {
                return null;
            }
            AnimationRunner animationRunner = new AnimationRunner(
                    (ExpandableNotificationRow) sourceView);
            return new RemoteAnimationAdapter(animationRunner, ANIMATION_DURATION,
                    ANIMATION_DURATION - 150 /* statusBarTransitionDelay */);
        }
    

    其中AnimationRunner是ActivityLaunchAnimator的内部类,其继承了IRemoteAnimationRunner.Stub:

        class AnimationRunner extends IRemoteAnimationRunner.Stub {
    
            private final ExpandableNotificationRow mSourceNotification;
            private final ExpandAnimationParameters mParams;
            private final Rect mWindowCrop = new Rect();
            private final float mNotificationCornerRadius;
            private float mCornerRadius;
            private boolean mIsFullScreenLaunch = true;
            private final SyncRtSurfaceTransactionApplier mSyncRtTransactionApplier;
    
            public AnimationRunner(ExpandableNotificationRow sourceNofitication) {
                mSourceNotification = sourceNofitication;
                mParams = new ExpandAnimationParameters();
                mSyncRtTransactionApplier = new SyncRtSurfaceTransactionApplier(mSourceNotification);
                mNotificationCornerRadius = Math.max(mSourceNotification.getCurrentTopRoundness(),
                        mSourceNotification.getCurrentBottomRoundness());
            }
    
            @Override
            public void onAnimationStart(RemoteAnimationTarget[] remoteAnimationTargets,
                    RemoteAnimationTarget[] remoteAnimationWallpaperTargets,
                    IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
                        throws RemoteException {
                mMainExecutor.execute(() -> {
                    RemoteAnimationTarget primary = getPrimaryRemoteAnimationTarget(
                            remoteAnimationTargets);
                    if (primary == null) {
                        setAnimationPending(false);
                        invokeCallback(iRemoteAnimationFinishedCallback);
                        mNotificationPanel.collapse(false /* delayed */, 1.0f /* speedUpFactor */);
                        return;
                    }
    
                    setExpandAnimationRunning(true);
                    mIsFullScreenLaunch = primary.position.y == 0
                            && primary.sourceContainerBounds.height()
                                    >= mNotificationPanel.getHeight();
                    if (!mIsFullScreenLaunch) {
                        mNotificationPanel.collapseWithDuration(ANIMATION_DURATION);
                    }
                    ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
                    mParams.startPosition = mSourceNotification.getLocationOnScreen();
                    mParams.startTranslationZ = mSourceNotification.getTranslationZ();
                    mParams.startClipTopAmount = mSourceNotification.getClipTopAmount();
                    if (mSourceNotification.isChildInGroup()) {
                        int parentClip = mSourceNotification
                                .getNotificationParent().getClipTopAmount();
                        mParams.parentStartClipTopAmount = parentClip;
                        // We need to calculate how much the child is clipped by the parent
                        // because children always have 0 clipTopAmount
                        if (parentClip != 0) {
                            float childClip = parentClip
                                    - mSourceNotification.getTranslationY();
                            if (childClip > 0.0f) {
                                mParams.startClipTopAmount = (int) Math.ceil(childClip);
                            }
                        }
                    }
                    int targetWidth = primary.sourceContainerBounds.width();
                    // If the notification panel is collapsed, the clip may be larger than the height.
                    int notificationHeight = Math.max(mSourceNotification.getActualHeight()
                            - mSourceNotification.getClipBottomAmount(), 0);
                    int notificationWidth = mSourceNotification.getWidth();
                    anim.setDuration(ANIMATION_DURATION);
                    anim.setInterpolator(Interpolators.LINEAR);
                    anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                        @Override
                        public void onAnimationUpdate(ValueAnimator animation) {
                            mParams.linearProgress = animation.getAnimatedFraction();
                            float progress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
                                            mParams.linearProgress);
                            int newWidth = (int) MathUtils.lerp(notificationWidth,
                                    targetWidth, progress);
                            mParams.left = (int) ((targetWidth - newWidth) / 2.0f);
                            mParams.right = mParams.left + newWidth;
                            mParams.top = (int) MathUtils.lerp(mParams.startPosition[1],
                                    primary.position.y, progress);
                            mParams.bottom = (int) MathUtils.lerp(mParams.startPosition[1]
                                            + notificationHeight,
                                    primary.position.y + primary.sourceContainerBounds.bottom,
                                    progress);
                            mCornerRadius = MathUtils.lerp(mNotificationCornerRadius,
                                    mWindowCornerRadius, progress);
                            applyParamsToWindow(primary);
                            applyParamsToNotification(mParams);
                            applyParamsToNotificationShade(mParams);
                        }
                    });
                    anim.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            setExpandAnimationRunning(false);
                            invokeCallback(iRemoteAnimationFinishedCallback);
                        }
                    });
                    anim.start();
                    setAnimationPending(false);
                });
            }
    
            private void invokeCallback(IRemoteAnimationFinishedCallback callback) {
                try {
                    callback.onAnimationFinished();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
    
            private RemoteAnimationTarget getPrimaryRemoteAnimationTarget(
                    RemoteAnimationTarget[] remoteAnimationTargets) {
                RemoteAnimationTarget primary = null;
                for (RemoteAnimationTarget app : remoteAnimationTargets) {
                    if (app.mode == RemoteAnimationTarget.MODE_OPENING) {
                        primary = app;
                        break;
                    }
                }
                return primary;
            }
    
            private void setExpandAnimationRunning(boolean running) {
                mNotificationPanel.setLaunchingNotification(running);
                mSourceNotification.setExpandAnimationRunning(running);
                mNotificationShadeWindowViewController.setExpandAnimationRunning(running);
                mNotificationContainer.setExpandingNotification(running ? mSourceNotification : null);
                mAnimationRunning = running;
                if (!running) {
                    mCallback.onExpandAnimationFinished(mIsFullScreenLaunch);
                    applyParamsToNotification(null);
                    applyParamsToNotificationShade(null);
                }
    
            }
    
            private void applyParamsToNotificationShade(ExpandAnimationParameters params) {
                mNotificationContainer.applyExpandAnimationParams(params);
                mNotificationPanel.applyExpandAnimationParams(params);
                mDepthController.setNotificationLaunchAnimationParams(params);
            }
    
            private void applyParamsToNotification(ExpandAnimationParameters params) {
                mSourceNotification.applyExpandAnimationParams(params);
            }
    
            private void applyParamsToWindow(RemoteAnimationTarget app) {
                Matrix m = new Matrix();
                m.postTranslate(0, (float) (mParams.top - app.position.y));
                mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
                SurfaceParams params = new SurfaceParams.Builder(app.leash)
                        .withAlpha(1f)
                        .withMatrix(m)
                        .withWindowCrop(mWindowCrop)
                        .withLayer(app.prefixOrderIndex)
                        .withCornerRadius(mCornerRadius)
                        .withVisibility(true)
                        .build();
                mSyncRtTransactionApplier.scheduleApply(params);
            }
    
            @Override
            public void onAnimationCancelled() throws RemoteException {
                mMainExecutor.execute(() -> {
                    setAnimationPending(false);
                    mCallback.onLaunchAnimationCancelled();
                });
            }
        };
    

    在StatusBarNotificationActivityStarter的startNotificationIntent方法中,创建好了RemoteAnimationAdapter后,通过binder调用了ActivityTaskManagerService的registerRemoteAnimationForNextActivityStart方法:

        @Override
        public void registerRemoteAnimationForNextActivityStart(String packageName,
                RemoteAnimationAdapter adapter) {
            mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
                    "registerRemoteAnimationForNextActivityStart");
            adapter.setCallingPidUid(Binder.getCallingPid(), Binder.getCallingUid());
            synchronized (mGlobalLock) {
                final long origId = Binder.clearCallingIdentity();
                try {
                    getActivityStartController().registerRemoteAnimationForNextActivityStart(
                            packageName, adapter);
                } finally {
                    Binder.restoreCallingIdentity(origId);
                }
            }
        }
    

    调用了ActivityStartController的registerRemoteAnimationForNextActivityStart方法:

        void registerRemoteAnimationForNextActivityStart(String packageName,
                RemoteAnimationAdapter adapter) {
            mPendingRemoteAnimationRegistry.addPendingAnimation(packageName, adapter);
        }
    

    调用了PendingRemoteAnimationRegistry的addPendingAnimation方法:

        private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
    
        /**
         * Adds a remote animation to be run for all activity starts originating from a certain package.
         */
        void addPendingAnimation(String packageName, RemoteAnimationAdapter adapter) {
            mEntries.put(packageName, new Entry(packageName, adapter));
        }
    

    最终将RemoteAnimationAdapter保存到了ActivityTaskManagerService.mActivityStartController.mPendingRemoteAnimationRegistry.mEntries中。

    2 在ActivityStarter启动Activity时,将RemoteAnimationAdapter保存在ActivityOptions中

    在ActivityStarter的executeRequest方法中:

        /**
         * Executing activity start request and starts the journey of starting an activity. Here
         * begins with performing several preliminary checks. The normally activity launch flow will
         * go through {@link #startActivityUnchecked} to {@link #startActivityInner}.
         */
        private int executeRequest(Request request) {
            ......
            if (request.allowPendingRemoteAnimationRegistryLookup) {
                checkedOptions = mService.getActivityStartController()
                        .getPendingRemoteAnimationRegistry()
                        .overrideOptionsIfNeeded(callingPackage, checkedOptions);
            }
            ......
            final ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
                    callingPackage, callingFeatureId, intent, resolvedType, aInfo,
                    mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode,
                    request.componentSpecified, voiceSession != null, mSupervisor, checkedOptions,
                    sourceRecord);
            ......
        }
    

    调用了PendingRemoteAnimationRegistry的overrideOptionsIfNeeded方法:

        /**
         * Overrides the activity options with a registered remote animation for a certain calling
         * package if such a remote animation is registered.
         */
        ActivityOptions overrideOptionsIfNeeded(String callingPackage,
                @Nullable ActivityOptions options) {
            final Entry entry = mEntries.get(callingPackage);
            if (entry == null) {
                return options;
            }
            if (options == null) {
                options = ActivityOptions.makeRemoteAnimation(entry.adapter);
            } else {
                options.setRemoteAnimationAdapter(entry.adapter);
            }
            mEntries.remove(callingPackage);
            return options;
        }
    

    将RemoteAnimationAdapter保存在了ActivityOptions中。

    在ActivityStarter的executeRequest方法后面还调用了ActivityRecord的构造器:

        ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,
                int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage,
                @Nullable String _launchedFromFeature, Intent _intent, String _resolvedType,
                ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo,
                String _resultWho, int _reqCode, boolean _componentSpecified,
                boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor,
                ActivityOptions options, ActivityRecord sourceRecord) {
            ......
            if (options != null) {
                pendingOptions = options;
            ......
        }
    

    将这个ActivityOptions保存在了ActivityRecord的成员变量pendingOptions中。

    3 ATMS将桌面Pause后,要对新的Activity进行Resume前,检查ActivityOptions时,将RemoteAnimationAdapter保存在了AppTransition的RemoteAnimationController中

    先看ActivityTaskManagerService的activityPaused方法:

        @Override
        public final void activityPaused(IBinder token) {
            final long origId = Binder.clearCallingIdentity();
            synchronized (mGlobalLock) {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityPaused");
                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
                if (r != null) {
                    r.activityPaused(false);
                }
                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            }
            Binder.restoreCallingIdentity(origId);
        }
    

    其中r就是桌面的ActivityRecord,调用了其activityPaused方法:

        void activityPaused(boolean timeout) {
            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE,
                    "Activity paused: token=" + appToken + ", timeout=" + timeout);
    
            final ActivityStack stack = getStack();
    
            if (stack != null) {
                removePauseTimeout();
    
                if (stack.mPausingActivity == this) {
                    if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + this
                            + (timeout ? " (due to timeout)" : " (pause complete)"));
                    mAtmService.deferWindowLayout();
                    try {
                        stack.completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
                    } finally {
                        mAtmService.continueWindowLayout();
                    }
                    return;
                } else {
                    EventLogTags.writeWmFailedToPause(mUserId, System.identityHashCode(this),
                            shortComponentName, stack.mPausingActivity != null
                                    ? stack.mPausingActivity.shortComponentName : "(none)");
                    if (isState(PAUSING)) {
                        callServiceTrackeronActivityStatechange(PAUSED, true);
                        setState(PAUSED, "activityPausedLocked");
                        if (finishing) {
                            if (DEBUG_PAUSE) Slog.v(TAG,
                                    "Executing finish of failed to pause activity: " + this);
                            completeFinishing("activityPausedLocked");
                        }
                    }
                }
            }
    
            mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
        }
    

    又调用了其ActivityStack的completePauseLocked方法:

        @VisibleForTesting
        void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
            ActivityRecord prev = mPausingActivity;
            ......
            if (resumeNext) {
                final ActivityStack topStack = mRootWindowContainer.getTopDisplayFocusedStack();
                if (topStack != null && !topStack.shouldSleepOrShutDownActivities()) {
                    mRootWindowContainer.resumeFocusedStacksTopActivities(topStack, prev, null);
                }
                ......
            }
            ......
        }
    

    调用了RootWindowContainer的resumeFocusedStacksTopActivities方法:

        boolean resumeFocusedStacksTopActivities(
                ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
            ......
            if (targetStack != null && (targetStack.isTopStackInDisplayArea()
                    || getTopDisplayFocusedStack() == targetStack)) {
                result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
            }
            ......
        }
    

    调用了ActivityStack的resumeTopActivityUncheckedLocked方法:

        /**
         * Ensure that the top activity in the stack is resumed.
         *
         * @param prev The previously resumed activity, for when in the process
         * of pausing; can be null to call from elsewhere.
         * @param options Activity options.
         *
         * @return Returns true if something is being resumed, or false if
         * nothing happened.
         *
         * NOTE: It is not safe to call this method directly as it can cause an activity in a
         *       non-focused stack to be resumed.
         *       Use {@link RootWindowContainer#resumeFocusedStacksTopActivities} to resume the
         *       right activity for the current system state.
         */
        @GuardedBy("mService")
        boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
            ......
            try {
                ......
                result = resumeTopActivityInnerLocked(prev, options);
                ......
            }
            ......
        }
    

    调用了ActivityStack的resumeTopActivityInnerLocked方法:

        @GuardedBy("mService")
        private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
            ......
            if (anim) {
                next.applyOptionsLocked();
            }
            ......
        }
    

    其中next是要启动的Activity的ActivityRecord,调用了其applyOptionsLocked方法:

        void applyOptionsLocked() {
            if (pendingOptions != null
                    && pendingOptions.getAnimationType() != ANIM_SCENE_TRANSITION) {
                ......
                applyOptionsLocked(pendingOptions, intent);
                ......
            }
        }
    
        /**
         * Apply override app transition base on options & animation type.
         */
        void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) {
            final int animationType = pendingOptions.getAnimationType();
            final DisplayContent displayContent = getDisplayContent();
            
            switch (animationType) {
                ......
                case ANIM_REMOTE_ANIMATION:
                    displayContent.mAppTransition.overridePendingAppTransitionRemote(
                            pendingOptions.getRemoteAnimationAdapter());
                    break;
                ......
            }
        }
    

    根据上一步可知,这里的pendingOptions中保存了systemui进程传来的RemoteAnimationAdapter。

    最后调用了AppTransition的overridePendingAppTransitionRemote方法:

        void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
            ProtoLog.i(WM_DEBUG_APP_TRANSITIONS, "Override pending remote transitionSet=%b adapter=%s",
                            isTransitionSet(), remoteAnimationAdapter);
            if (isTransitionSet()) {
                clear();
                mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
                mRemoteAnimationController = new RemoteAnimationController(mService,
                        remoteAnimationAdapter, mHandler);
            }
        }
    

    在这里创建了RemoteAnimationController,其中保存了RemoteAnimationAdapter。

    4 在app过渡动画流程中,使用RemoteAnimationAdapterWrapper作为AnimationAdapter

    在WindowContainer的applyAnimationUnchecked方法中调用了WindowContainer的getAnimationAdapter方法:

        /**
         * Gets the {@link AnimationAdapter} according the given window layout properties in the window
         * hierarchy.
         *
         * @return The return value will always contain two elements, one for normal animations and the
         *         other for thumbnail animation, both can be {@code null}.
         *
         * @See com.android.server.wm.RemoteAnimationController.RemoteAnimationRecord
         * @See LocalAnimationAdapter
         */
        Pair<AnimationAdapter, AnimationAdapter> getAnimationAdapter(WindowManager.LayoutParams lp,
                int transit, boolean enter, boolean isVoiceInteraction) {
            final Pair<AnimationAdapter, AnimationAdapter> resultAdapters;
            ......
            final RemoteAnimationController controller =
                    getDisplayContent().mAppTransition.getRemoteAnimationController();
            ......
            // Delaying animation start isn't compatible with remote animations at all.
            if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) {
                final Rect localBounds = new Rect(mTmpRect);
                localBounds.offsetTo(mTmpPoint.x, mTmpPoint.y);
                final RemoteAnimationController.RemoteAnimationRecord adapters =
                        controller.createRemoteAnimationRecord(this, mTmpPoint, localBounds,
                                screenBounds, (isChanging ? mSurfaceFreezer.mFreezeBounds : null));
                resultAdapters = new Pair<>(adapters.mAdapter, adapters.mThumbnailAdapter);
            }
            ......
            return resultAdapters;
        }
    

    其中调用了RemoteAnimationController的createRemoteAnimationRecord方法:

        /**
         * Creates an animation record for each individual {@link WindowContainer}.
         *
         * @param windowContainer The windows to animate.
         * @param position The position app bounds, in screen coordinates.
         * @param localBounds The bounds of the app relative to its parent.
         * @param stackBounds The stack bounds of the app relative to position.
         * @param startBounds The stack bounds before the transition, in screen coordinates
         * @return The record representing animation(s) to run on the app.
         */
        RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,
                Point position, Rect localBounds, Rect stackBounds, Rect startBounds) {
            ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s",
                    windowContainer);
            final RemoteAnimationRecord adapters = new RemoteAnimationRecord(windowContainer, position,
                    localBounds, stackBounds, startBounds);
            mPendingAnimations.add(adapters);
            return adapters;
        }
    

    RemoteAnimationRecord是RemoteAnimationController的内部类:

        /**
         * Contains information about a remote-animation for one WindowContainer. This keeps track of,
         * potentially, multiple animating surfaces (AdapterWrappers) associated with one
         * Window/Transition. For example, a change transition has an adapter controller for the
         * main window and an adapter controlling the start-state snapshot.
         * <p>
         * This can be thought of as a bridge between the information that the remote animator sees (via
         * {@link RemoteAnimationTarget}) and what the server sees (the
         * {@link RemoteAnimationAdapterWrapper}(s) interfacing with the moving surfaces).
         */
        public class RemoteAnimationRecord {
            RemoteAnimationAdapterWrapper mAdapter;
            RemoteAnimationAdapterWrapper mThumbnailAdapter = null;
            RemoteAnimationTarget mTarget;
            final WindowContainer mWindowContainer;
            final Rect mStartBounds;
    
            RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,
                    Rect endBounds, Rect startBounds) {
                mWindowContainer = windowContainer;
                mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds);
                if (startBounds != null) {
                    mStartBounds = new Rect(startBounds);
                    mTmpRect.set(startBounds);
                    mTmpRect.offsetTo(0, 0);
                    if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {
                        mThumbnailAdapter =
                                new RemoteAnimationAdapterWrapper(this, new Point(0, 0), localBounds,
                                        mTmpRect);
                    }
                } else {
                    mStartBounds = null;
                }
            }
    
            RemoteAnimationTarget createRemoteAnimationTarget() {
                if (mAdapter == null
                        || mAdapter.mCapturedFinishCallback == null
                        || mAdapter.mCapturedLeash == null) {
                    return null;
                }
                mTarget = mWindowContainer.createRemoteAnimationTarget(this);
                return mTarget;
            }
    
            int getMode() {
                final DisplayContent dc = mWindowContainer.getDisplayContent();
                final ActivityRecord topActivity = mWindowContainer.getTopMostActivity();
                // Note that opening/closing transitions are per-activity while changing transitions
                // are per-task.
                if (dc.mOpeningApps.contains(topActivity)) {
                    return RemoteAnimationTarget.MODE_OPENING;
                } else if (dc.mChangingContainers.contains(mWindowContainer)) {
                    return RemoteAnimationTarget.MODE_CHANGING;
                } else {
                    return RemoteAnimationTarget.MODE_CLOSING;
                }
            }
        }
    

    RemoteAnimationAdapterWrapper是RemoteAnimationController的内部类,其实现了AnimationAdapter接口:

        class RemoteAnimationAdapterWrapper implements AnimationAdapter {
            private final RemoteAnimationRecord mRecord;
            SurfaceControl mCapturedLeash;
            private OnAnimationFinishedCallback mCapturedFinishCallback;
            private @AnimationType int mAnimationType;
            final Point mPosition = new Point();
            final Rect mLocalBounds;
            final Rect mStackBounds = new Rect();
    
            RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position,
                    Rect localBounds, Rect stackBounds) {
                mRecord = record;
                mPosition.set(position.x, position.y);
                mLocalBounds = localBounds;
                mStackBounds.set(stackBounds);
            }
    
            @Override
            public boolean getShowWallpaper() {
                return false;
            }
    
            @Override
            public void startAnimation(SurfaceControl animationLeash, Transaction t,
                    @AnimationType int type, OnAnimationFinishedCallback finishCallback) {
                ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");
    
                // Restore position and stack crop until client has a chance to modify it.
                if (mRecord.mStartBounds != null) {
                    t.setPosition(animationLeash, mRecord.mStartBounds.left, mRecord.mStartBounds.top);
                    t.setWindowCrop(animationLeash, mRecord.mStartBounds.width(),
                            mRecord.mStartBounds.height());
                } else {
                    t.setPosition(animationLeash, mPosition.x, mPosition.y);
                    t.setWindowCrop(animationLeash, mStackBounds.width(), mStackBounds.height());
                }
                mCapturedLeash = animationLeash;
                mCapturedFinishCallback = finishCallback;
                mAnimationType = type;
            }
    
            @Override
            public void onAnimationCancelled(SurfaceControl animationLeash) {
                if (mRecord.mAdapter == this) {
                    mRecord.mAdapter = null;
                } else {
                    mRecord.mThumbnailAdapter = null;
                }
                if (mRecord.mAdapter == null && mRecord.mThumbnailAdapter == null) {
                    mPendingAnimations.remove(mRecord);
                }
                if (mPendingAnimations.isEmpty()) {
                    cancelAnimation("allAppAnimationsCanceled");
                }
            }
    
            @Override
            public long getDurationHint() {
                return mRemoteAnimationAdapter.getDuration();
            }
    
            @Override
            public long getStatusBarTransitionsStartTime() {
                return SystemClock.uptimeMillis()
                        + mRemoteAnimationAdapter.getStatusBarTransitionDelay();
            }
    
            @Override
            public void dump(PrintWriter pw, String prefix) {
                pw.print(prefix); pw.print("container="); pw.println(mRecord.mWindowContainer);
                if (mRecord.mTarget != null) {
                    pw.print(prefix); pw.println("Target:");
                    mRecord.mTarget.dump(pw, prefix + "  ");
                } else {
                    pw.print(prefix); pw.println("Target: null");
                }
            }
    
            @Override
            public void dumpDebug(ProtoOutputStream proto) {
                final long token = proto.start(REMOTE);
                if (mRecord.mTarget != null) {
                    mRecord.mTarget.dumpDebug(proto, TARGET);
                }
                proto.end(token);
            }
        }
    

    5 调用IRemoteAnimationRunner的onAnimationStart方法开始进行远程动画

    在AppTransitionController的handleAppTransitionReady方法后面还调用了AppTransition的goodToGo方法:

        /**
         * @return bit-map of WindowManagerPolicy#FINISH_LAYOUT_REDO_* to indicate whether another
         *         layout pass needs to be done
         */
        int goodToGo(int transit, ActivityRecord topOpeningApp, ArraySet<ActivityRecord> openingApps) {
            ......
            if (mRemoteAnimationController != null) {
                mRemoteAnimationController.goodToGo();
            }
            ......
        }
    

    调用了RemoteAnimationController的goodToGo方法:

        /**
         * Called when the transition is ready to be started, and all leashes have been set up.
         */
        void goodToGo() {
            ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()");
            if (mPendingAnimations.isEmpty() || mCanceled) {
                ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
                        "goodToGo(): Animation finished already, canceled=%s mPendingAnimations=%d",
                        mCanceled, mPendingAnimations.size());
                onAnimationFinished();
                return;
            }
    
            // Scale the timeout with the animator scale the controlling app is using.
            mHandler.postDelayed(mTimeoutRunnable,
                    (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
            mFinishedCallback = new FinishedCallback(this);
    
            // Create the app targets
            final RemoteAnimationTarget[] appTargets = createAppAnimations();
            if (appTargets.length == 0) {
                ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo(): No apps to animate");
                onAnimationFinished();
                return;
            }
    
            // Create the remote wallpaper animation targets (if any)
            final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
            mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
                try {
                    linkToDeathOfRunner();
                    mRemoteAnimationAdapter.getRunner().onAnimationStart(appTargets, wallpaperTargets,
                            mFinishedCallback);
                } catch (RemoteException e) {
                    Slog.e(TAG, "Failed to start remote animation", e);
                    onAnimationFinished();
                }
                if (ProtoLogImpl.isEnabled(WM_DEBUG_REMOTE_ANIMATIONS)) {
                    ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation(): Notify animation start:");
                    writeStartDebugStatement();
                }
            });
            setRunningRemoteAnimation(true);
        }
    

    最终调用了IRemoteAnimationRunner的onAnimationStart方法开始进行远程动画。

    展开全文
  • Qt 实现自定义动画属性窗口大小 过渡动画
  • Visual Studio Code的SmoothType此扩展添加了平滑键入的动画,类似于MS Office和Windows 10 Mail应用程序。 从Visual Studio市场获取。 注意:请向下滚动并阅读Visual Studio代码的labe SmoothType部分声明注意:...
  • win7系统使用久了,好多网友反馈说win7系统窗口动画慢动作功能开启的问题,非常不方便。有什么办法可以永久解决win7系统窗口动画慢动作功能开启的问题,面对win7系统窗口动画慢动作功能开启的图文步骤非常简单,只...
  • 原文地址:Windows Insets + Fragment Transitions: A tale of woe 原文作者:Chris Banes 译文出自:掘金翻译计划 ...这篇文章是我写的关于 fragment 过渡动画的小系列中的第二篇。第一篇可以通过下面的...
  • 您好 Windows 预览体验成员,我们很高兴向开发频道发布第一个 Windows 11 Insider Preview 版本 Build 22000.51!随着我们在未来几个月内最终确定产品,我们将与您一起验证体验。您将可以尝试许多(但不是全部)我们...
  • OpenShot Video Editor是一款屡获殊荣的免费,开源Linux,Mac和Windows视频编辑器,致力于为世界提供高质量的视频编辑和动画解决方案。 建立状态 产品特点 跨平台(Linux,Mac和Windows) 支持多种视频,音频和图像...
  • WPF:(四)常见的动画及案例

    万次阅读 2019-06-25 22:26:53
    计算机中的动画一般是定格动画,也称之为逐帧动画,它通过每帧不同的图像连续播放,从而欺骗眼和脑产生动画效果。 二. WPF的动画的实现 (1)简洁 这个是非常明显的,WPF的动画的代码非常容易理解,Timer的版本则要...
  • Flutter 2.10 稳定版于2月4日发布,首次实现Windows应用程序的稳定支持。 Flutter旨在创建高效跨平台软件框架,在过去几年取得了长足发展。Flutter可为 Android、iOS、Linux、Windows、macOS 以及网页开发应用,并...
  • 要下载适用于PC的Kinemaster(Windows 10 / 8.1 / 8/7 / Vista / XP和Mac),请转到网站提供的链接。 这是最好的视频编辑应用程序。 Kinemaster是一个免费平台,可为您的企业或任何其他类型的工作创建专业视频。 ...
  •  ☻TurnstilTransition 翻页动画动画  ☻RollTransition 360度旋转动画   5.添加动画效果,以其中的一种动画效果为例 SlideTransition Mode= " SlideLeftFadeIn " /> SlideTransition...
  • 原标题:电脑如何开启窗口动画慢动作效果?在电脑win10系统中,有一个窗口动画慢动作的功能,这项功能开启之后,电脑桌面窗口动画的速度就会变慢,犹如慢动作。而这个功能,大多是开发者用于测试动画效果的,那么这个...
  • 当ListBox中有多条数据,假设删除第二条时,第三,第四条会突然出现在原本第二,第三条的位置上面,中间没有过渡预期效果.我们现在就给ListBox添加这一效果 1.首先在MainPage中创建一个ListBox. 2.然后使用Blend...
  • 如何实现一个 windows 桌面动态壁纸

    千次阅读 2020-11-25 03:53:47
    这是一条神奇的 message,是 windows 为了解决切换壁纸的时候丑陋的闪切而创建的(未公开消息),它使得更换壁纸的时候有一个平滑的过渡但又不影响绘制壁纸,它会分离创建两个 WorkerW,我们只需要隐藏没有 ...
  • 并且它模拟实现了animate.css90%的动画效果,您可以轻松地把 web 页面端的动画效果转为视频。FFCreator的图形渲染部分使用的是渲染引擎 inkpaint https://github.com/drawcall/inkpaint。github:ht
  • iOS 更新 除了性能改进之外,我们还添加了一些特定平台的增强功能,其中一项新增的功能是来自luckysmg的 iOS 中更流畅的键盘动画,它会默认被应用于你的 App 而无需你做任何事情。 我们还通过修复一些边缘条件下...
  • 从菜单栏中打开“更改过渡”窗格,或按键盘快捷键Ctrl + Alt + Shift + B(Windows / Chrome OS)或Cmd + Option + Shift + B(macOS)将动画添加到对象。 Click on an object you want to animate and then click ...
  • 调整Ubuntu和Windows双系统时钟不同步问题6.截图工具二、为了丰富系统我会做的一些事情1.安装谷歌chrome浏览器2.安装QQ音乐/网易云音乐3.安装deepin-wine 的 QQ 和 微信4.安装VLC影音播放器5.安装Microsoft Windows ...
  • 本文译自Nick Waggoner的"Understand what’s possible with the Windows UI Animation Engine",已获原作者授权进行翻译。更多有关 Windows UI、UWP 开发的文章,欢迎访问我的博客源站:http://validvoid.net/ ...
  • C#WPFXAML动画

    2021-10-11 18:17:11
    C#WPF XAML 动画 一、动画(Animatuon) I.WPF三种动画: 1.(1)线性插值:在开始值和结束值之间以逐步增加的方式改变属性的动画(线性插值过程)。 (2)、关键帧:从一个值突然变成另...(3)、路径:在System.Windows.Med

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 9,063
精华内容 3,625
热门标签
关键字:

windows过渡动画