>  기사  >  백엔드 개발  >  UWP에서 한도를 달성하기 위해 Composition API를 사용하는 방법 소개(2)

UWP에서 한도를 달성하기 위해 Composition API를 사용하는 방법 소개(2)

零下一度
零下一度원래의
2017-06-26 15:38:231875검색

이전 글에서는 Pivot이 포함되지 않은 천장 작업에 대해 설명했지만 일반적으로 천장 부분은 Pivot의 Header이므로 여기에서는 동일한 Header 조건에 여러 개의 Pivot 항목이 어떻게 연결되는지 설명하겠습니다.

먼저 간단한 페이지를 만듭니다. 페이지에는 머리글로 그리드가 있고 머리가 제거된 피벗이 있으며 피벗에는 3개의 ListView가 있습니다. 페이지 헤더.

<Pagex:Class="TestListViewHeader.TestHeader2"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:TestListViewHeader"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"mc:Ignorable="d"><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><Pivot ItemsSource="{x:Bind ItemSource}" x:Name="_pivot" SelectionChanged="_pivot_SelectionChanged" ><Pivot.Template>               <!--太长在这儿就不贴了--></Pivot.Template><Pivot.HeaderTemplate><DataTemplate></DataTemplate></Pivot.HeaderTemplate><Pivot.ItemTemplate><DataTemplate><ListView ItemsSource="{Binding }"><ListView.Header><Grid Height="150" /></ListView.Header><ListView.ItemTemplate><DataTemplate><TextBlock Text="{Binding }" /></DataTemplate></ListView.ItemTemplate></ListView></DataTemplate></Pivot.ItemTemplate></Pivot><Grid Height="150" VerticalAlignment="Top" x:Name="_header"><Grid.RowDefinitions><RowDefinition Height="100" /><RowDefinition Height="50" /></Grid.RowDefinitions><Grid Background="LightBlue"><TextBlock FontSize="30" VerticalAlignment="Center" HorizontalAlignment="Center">我会被隐藏</TextBlock></Grid><Grid Grid.Row="1"><ListBox SelectedIndex="{x:Bind _pivot.SelectedIndex,Mode=TwoWay}" ItemsSource="{x:Bind ItemSource}"><ListBox.ItemTemplate><DataTemplate><Grid><TextBlock Text="{Binding Title}" /></Grid></DataTemplate></ListBox.ItemTemplate><ListBox.ItemsPanel><ItemsPanelTemplate><VirtualizingStackPanel Orientation="Horizontal" /></ItemsPanelTemplate></ListBox.ItemsPanel></ListBox></Grid></Grid></Grid></Page>

Pivot의 템플릿이 너무 길어서 여기에 쓰지 않겠습니다. 필요한 경우 시스템에 내장된 브러시 리소스를 찾아 F12를 눌러 generic.xaml을 연 다음 Pivot에서 다른 컨트롤을 검색할 수 있습니다. 이 방법을 통해서도 얻을 수 있습니다.

템플릿에서 다음 문장을 수정하여 헤더를 제거할 수 있습니다.

<PivotPanel x:Name="Panel" VerticalAlignment="Stretch"><Grid x:Name="PivotLayoutElement"><Grid.RowDefinitions><RowDefinition Height="0" /><RowDefinition Height="*" /><!--太长不写--></Grid.RowDefinitions>

그런 다음 배경 코드가 있습니다. 이전 기사의 FindFirstChild 메서드도 여기에 사용되므로 여기에 게시하지 않겠습니다.

평소와 마찬가지로 전역 _headerVisual은 페이지의 Loaded 이벤트에서 필요한 변수를 초기화하는 것이 가장 좋습니다. 저는 게으르게 이를 아래의 UpdateAnimation 메서드에 직접 넣습니다.
그런 다음 PivotItem이 전환될 때 애니메이션 매개변수를 업데이트하는 UpdateAnimation 메서드를 작성합니다.

먼저 페이지가 선택되지 않았는지 확인하고 돌아간 다음 현재 선택된 항목의 컨테이너를 가져온 다음 지난번처럼 컨테이너에서 ScrollViewer를 가져오는데 여기에 함정이 있으니 나중에 다루겠습니다.

void UpdateAnimation()
{if (_pivot.SelectedIndex == -1) return;var SelectionItem = _pivot.ContainerFromIndex(_pivot.SelectedIndex) as PivotItem;if (SelectionItem == null) return;var _scrollviewer = FindFirstChild<ScrollViewer>(SelectionItem);if (_scrollviewer != null)
    {
        _headerVisual = ElementCompositionPreview.GetElementVisual(_header);var _manipulationPropertySet = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(_scrollviewer);var _compositor = Window.Current.Compositor;var line = _compositor.CreateCubicBezierEasingFunction(new System.Numerics.Vector2(0, 0), new System.Numerics.Vector2(0.6f, 1));var _headerAnimation = _compositor.CreateExpressionAnimation("_manipulationPropertySet.Translation.Y > -100f ? _manipulationPropertySet.Translation.Y: -100f");
        _headerAnimation.SetReferenceParameter("_manipulationPropertySet", _manipulationPropertySet);
        _headerVisual.StartAnimation("Offset.Y", _headerAnimation);
    }
}

그런 다음 Pivot의 SelectionChanged 이벤트에서 애니메이션을 업데이트합니다.

private void _pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    UpdateAnimation();
}

클릭하여 실행하고 위아래로 슬라이드하지만 움직이지는 않습니다. 왼쪽과 오른쪽으로 전환한 후 두 번째로 PivotItem으로 전환했을 때 움직임을 따라갈 수 있음을 발견했습니다. 처음으로 "var _scrollviewer = FindFirstChild(SelectionItem);"을 실행했을 때 _scrollviewer가 null인 것을 확인했습니다. 시간. 오랫동안 고민한 끝에 깨달았습니다. 컨트롤이 로드되지 않아 자식 컨트롤을 얻을 수 없는 것이 문제일까요? 바꾸라고 하면 바꾸세요.

void UpdateAnimation()
{if (_pivot.SelectedIndex == -1) return;var SelectionItem = _pivot.ContainerFromIndex(_pivot.SelectedIndex) as PivotItem;if (SelectionItem == null) return;var _scrollviewer = FindFirstChild<ScrollViewer>(SelectionItem);if (_scrollviewer != null)
    {
        _headerVisual = ElementCompositionPreview.GetElementVisual(_header);var _manipulationPropertySet = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(_scrollviewer);var _compositor = Window.Current.Compositor;var line = _compositor.CreateCubicBezierEasingFunction(new System.Numerics.Vector2(0, 0), new System.Numerics.Vector2(0.6f, 1));var _headerAnimation = _compositor.CreateExpressionAnimation("_manipulationPropertySet.Translation.Y > -100f ? _manipulationPropertySet.Translation.Y: -100f");
        _headerAnimation.SetReferenceParameter("_manipulationPropertySet", _manipulationPropertySet);
        _headerVisual.StartAnimation("Offset.Y", _headerAnimation);
    }elseSelectionItem.Loaded += (s, a) =>{
            UpdateAnimation();
        };
}

다시 달리면서 동작을 따라해보세요. 하지만 여전히 문제가 있습니다. 헤더는 전환될 때마다 원래 위치로 돌아갑니다. 이것은 또 다른 함정입니다.
PivotItem을 전환하면 _manipulationPropertySet.Translation.Y가 순간적으로 0이 될 것 같아요. 내가 밟은 함정을 또 밟지 마세요.
업데이트를 시도하기 전에 애니메이션을 중지하세요.

private void _pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    _headerVisual?.StopAnimation("Offset.Y");
    UpdateAnimation();
}

실행했는데 예상대로 실패했습니다.
이 순간 번쩍이는 영감이 떠올랐습니다. 애니메이션이 재생되는 데 시간이 걸립니다! 이 스위치의 애니메이션은 약 5단계입니다.

  1. 페이지가 왼쪽으로 이동하고

  2. 새 페이지를 로드합니다.

  3. 페이지가 오른쪽에서 이동하면서 중앙으로 이동하면서 서서히 등장합니다.
  4. SelectionChanged가 첫 번째 단계 전에 트리거된 다음 애니메이션을 중지하고 애니메이션을 업데이트했는데 내 표정 애니메이션이 재생되기 시작했지만 그의 첫 번째 단계는 여전히 느리고 완료되지 않았습니다...
  5. 간단합니다. SelectionChanged에 지연을 추가하세요. , 헤더 재배치 문제는 해결될 수 있습니다. (맞죠?) (여기 또 다른 함정이 있습니다):

    private async void _pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        _headerVisual?.StopAnimation("Offset.Y");await Task.Delay(180);
        UpdateAnimation();
    }
  6. 완벽하게 실행됩니다. 그러다가 휴대폰으로 시도해 봤는데 거의 울 뻔했어요.

    클릭 및 터치 조작 방식의 경우, 페이지 전환 시 이벤트 발생 순서와 애니메이션 재생 순서가 다릅니다!

    터치로 인한 페이지 전환은 대략 다음과 같습니다.


슬라이딩을 하면 페이지가 왼쪽으로 이동하고 점차 사라집니다.

SelectionChanged를 트리거합니다.

    새 페이지 로드
  1. 페이지가 오른쪽에서 중앙으로 이동하며 점차 나타납니다.
  2. 하지만 페이지가 사라진 후 _manipulationPropertySet.Translation.Y는 잠시 0이 됩니다! 이때는 정말 황당했지만 결국 해결책을 찾았습니다.
  3. _manipulationPropertySet.Translation.Y가 0이 되면 그냥 무시하세요. 이보다 더 영리할 수는 없습니다. 이렇게 하면 SelectionChanged에 지연을 작성할 필요가 없고, 갑자기 내 코드가 훨씬 더 우아해진다는 느낌이 듭니다.

    _headerAnimation의 표현식 수정:

  4.  _headerAnimation = _compositor.CreateExpressionAnimation(
  5. 참고: max, min 및 클램프는 모두 표현식 애니메이션에 내장된 함수입니다. 관련 정보는 부록을 참조하세요.

  6. 다시 시험을 치르고 완벽하게 통과하여 또 다른 구멍을 메웠습니다. 이 데모를 한동안 플레이한 후에도 여전히 몇 가지 단점이 있다는 것을 느꼈습니다. 페이지를 왼쪽과 오른쪽으로 전환할 때 머리를 위아래로 움직이는 것이 너무 뻣뻣합니다. 내 생각은 머리 위치 애니메이션을 조정하는 Complate 이벤트에서 머리 표현 애니메이션을 시작하는 것입니다.
  7. var line = _compositor.CreateCubicBezierEasingFunction(new System.Numerics.Vector2(0, 0), new System.Numerics.Vector2(0.6f, 1));var MoveHeaderAnimation = _compositor.CreateScalarKeyFrameAnimation();
    MoveHeaderAnimation.InsertExpressionKeyFrame(0f, "_headerVisual.Offset.Y", line);
    MoveHeaderAnimation.InsertExpressionKeyFrame(1f, "_manipulationPropertySet.Translation.Y > -100f ? _manipulationPropertySet.Translation.Y: -100f", line);
    MoveHeaderAnimation.SetReferenceParameter("_headerVisual", _headerVisual);
    MoveHeaderAnimation.SetReferenceParameter("_manipulationPropertySet", _manipulationPropertySet);
    MoveHeaderAnimation.DelayTime = TimeSpan.FromSeconds(0.18d);
    MoveHeaderAnimation.Duration = TimeSpan.FromSeconds(0.1d);

    创建一个关键帧动画,line是缓动效果。关键帧动画ScalarKeyFrameAnimation可以插入两种帧,一种是InsertKeyFrame(float,float,easingfunctuin),插入一个数值帧;一种是InsertExpressionKeyFrame(float,string,easingfunctuin),插入一个表达式帧,两者的第一个参数是进度,最小是0最大是1;第三个参数都是函数,可以设置为线性,贝塞尔曲线函数和步进。

    这时候就又发现了一个惊!天!大!秘!密!
    CompositionAnimation和CompositionAnimationGroup是没有Complated事件的!
    只能手动给延时了。然后...
    表达式动画不!支!持!延!时!好尴尬。

    同样是动画,看看隔壁家的StoryBoard,CompositionAnimation你们羞愧不羞愧。

    经过一番必应之后,我发现我错怪了他们,CompositionAnimation也可以做到Complated事件,只是方法有些曲折而已。

    动画完成事件

    通过使用关键帧动画,开发人员可以在完成精选动画(或动画组)时使用动画批来进行聚合。 仅可以批处理关键帧动画完成事件。 表达式动画没有一个确切终点,因此它们不会引发完成事件。 如果表达式动画在批中启动,该动画将会像预期那样执行,并且不会影响引发批的时间。

    当批内的所有动画都完成时,将引发批完成事件。 引发批的事件所需的时间取决于该批中时长最长的动画或延迟最为严重的动画。 在你需要了解选定的动画组将于何时完成以便计划一些其他工作时,聚合结束状态非常有用。

    批在引发完成事件后释放。 还可以随时调用 Dispose() 来尽早释放资源。 如果批处理的动画结束较早,并且你不希望继续完成事件,你可能会想要手动释放批对象。 如果动画已中断或取消,将会引发完成事件,并且该事件会计入设置它的批。 

    在动画开始前,新建一个ScopedBatch对象,然后播放动画,紧接着关闭ScopedBatch,动画运行完之后就会触发ScopedBatch的Completed事件。在ScopedBatch处于运行状态时,会收集所有动画,关闭后开始监视动画的进度。说的云里来雾里去的,还是看代码吧。

    var Betch = _compositor.CreateScopedBatch(Windows.UI.Composition.CompositionBatchTypes.Animation);
    _headerVisual.StartAnimation("Offset.Y", MoveHeaderAnimation);
    Betch.Completed += (s, a) =>{var _headerAnimation = _compositor.CreateExpressionAnimation("_manipulationPropertySet.Translation.Y > -100f ? (_manipulationPropertySet.Translation.Y == 0?This.CurrentValue :_manipulationPropertySet.Translation.Y) : -100f");//_manipulationPropertySet.Translation.Y是ScrollViewer滚动的数值,手指向上移动的时候,也就是可视部分向下移动的时候,Translation.Y是负数。_headerAnimation.SetReferenceParameter("_manipulationPropertySet", _manipulationPropertySet);
        _headerVisual.StartAnimation("Offset.Y", _headerAnimation);
    };
    Betch.End();

    我们把构造和播放_headerAnimation的代码放到了ScopedBatch的Complated事件里,这时再运行一下,完美。

    其实还是有点小问题,比如Header没有设置Clip,上下移动的时候有时会超出预期的范围之类的,有时间我们会继续讨论,这篇已经足够长,再长会吓跑人的。
    Demo已经放到Github,里面用到了一个写的很糙的滑动返回控件,等忙过这段时间整理下代码就开源,希望能有大牛指点一二。

    Github:

    总结一下,实现吸顶最核心的代码就是获取到ScrollViewer,不一定要是ListView的,明白了这一点,所有含有ScrollViewer的控件都可以放到这个这个页面使用。

    滑动返回:

위 내용은 UWP에서 한도를 달성하기 위해 Composition API를 사용하는 방법 소개(2)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.