Heim  >  Artikel  >  Backend-Entwicklung  >  Einführung in die Verwendung der Composition API zum Erreichen der Deckenhöhe in UWP (2)

Einführung in die Verwendung der Composition API zum Erreichen der Deckenhöhe in UWP (2)

零下一度
零下一度Original
2017-06-26 15:38:231909Durchsuche

Im vorherigen Artikel haben wir die Deckenoperation besprochen, die keinen Pivot beinhaltet, aber im Allgemeinen ist der Deckenteil der Header von Pivot, daher werden wir hier mehrere Aspekte von Pivot Item besprechen mit demselben Header verknüpft.

Erstellen Sie wie üblich zunächst eine einfache Seite. Die Seite hat ein Raster als Kopfzeile, einen Pivot mit entferntem Kopf und drei ListViews im Pivot auf die gleiche Höhe wie die Kopfzeile der Seite.

<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>

Die Vorlage von Pivot ist zu lang, daher werde ich sie hier nicht schreiben. Suchen Sie bei Bedarf nach einer im System integrierten Pinselressource und drücken Sie F12, um generic.xaml zu öffnen Suchen Sie dann nach Pivot. Mit dieser Methode können auch Vorlagen für andere Steuerelemente abgerufen werden.

Ändern Sie diese Sätze in der Vorlage, um den Header zu entfernen:

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

Dann wird auch der Hintergrundcode aus dem vorherigen Artikel verwendet hier werde ich es nicht posten.

Wie üblich, global _headerVisual, ist es am besten, die Variablen, die wir benötigen, im Loaded-Ereignis von Page zu initialisieren und sie direkt in die unten stehende UpdateAnimation-Methode einzufügen.
Dann schreiben wir eine UpdateAnimation-Methode, um die Animationsparameter zu aktualisieren, wenn das PivotItem wechselt.

Stellen Sie zunächst fest, ob die Seite nicht ausgewählt ist, und kehren Sie zurück. Rufen Sie dann den Container des aktuell ausgewählten Elements ab und rufen Sie dann wie beim letzten Mal den ScrollViewer aus dem Container ab. Hier gibt es jedoch eine Gefahr, über die wir sprechen werden es später.

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);
    }
}

Aktualisieren Sie dann die Animation im SelectionChanged-Ereignis von Pivot:

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

Zum Ausführen klicken, nach oben und unten schieben, und nichts passiert. Folgen Sie der Bewegung. Nachdem ich nach links und rechts gewechselt hatte, stellte ich fest, dass ich die Bewegung verfolgen konnte, als ich „var _scrollviewer = FindFirstChild“ zum ersten Mal ausführte Zeit. Nachdem ich lange darüber nachgedacht hatte, wurde mir klar, ob es ein Problem ist, dass die Steuerung nicht geladen ist und ich die untergeordnete Steuerung nicht erhalten kann. Wenn Sie Veränderung sagen, ändern Sie es.

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();
        };
}

Führen Sie es erneut aus und folgen Sie der Bewegung. Es gibt jedoch immer noch ein Problem. Der Header kehrt bei jedem Wechsel in seine ursprüngliche Position zurück. Dies ist eine weitere Gefahr.
Ich vermute, dass _manipulationPropertySet.Translation.Y beim Wechseln von PivotItem für einen Moment 0 wird. Bitte treten Sie nicht erneut auf die Fallstricke, auf die ich getreten bin.
Stoppen Sie die Animation, bevor Sie versuchen, sie zu aktualisieren.

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

Ausführen, es ist fehlgeschlagen.
In diesem Moment kam es zu einem Geistesblitz. Es braucht Zeit, bis die Animation abgespielt wird! Die Animation dieses Wechsels besteht aus etwa fünf Schritten:

  1. löst SelectionChanged aus;

  2. Die Seite bewegt sich nach links und verschwindet allmählich

  3. Seite entladen;

  4. Neue Seite laden

  5. Die Seite bewegt sich von rechts in die Mitte und erscheint nach und nach .

SelectionChanged wurde vor dem ersten Schritt ausgelöst, dann wurde die Animation gestoppt, die Animation aktualisiert und meine Ausdrucksanimation begann abzuspielen, aber sein erster Schritt war immer noch langsam und noch nicht abgeschlossen ... .
Einfach, das Hinzufügen einer Verzögerung zu SelectionChanged kann das Problem der Header-Neupositionierung lösen (hier ist eine weitere Gefahr):

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

Run, It's perfect. Dann versuchte ich es auf meinem Handy und weinte fast.
Bei Klick- und Berührungsvorgängen ist die Reihenfolge der Auslösung von Ereignissen und der Wiedergabe von Animationen beim Seitenwechsel unterschiedlich!
Der durch Berührung verursachte Seitenwechsel erfolgt in etwa in den folgenden Schritten:

  1. Durch das Verschieben verschiebt sich die Seite nach dem Loslassen nach links und verschwindet allmählich.

  2. Trigger-AuswahlGeändert;

  3. Neue Seite laden; 🎜>

    Seite von der rechten Seite bewegt sich in die Mitte und taucht nach und nach auf.
  4. Aber nachdem die Seite verschwindet, wird _manipulationPropertySet.Translation.Y für einen Moment zu 0! Ich war zu diesem Zeitpunkt wirklich am Boden zerstört, aber am Ende fand ich eine Lösung.
  5. Ignorieren Sie ihn einfach, wenn _manipulationPropertySet.Translation.Y 0 wird. Cleverer geht es nicht. Auf diese Weise ist es nicht nötig, eine Verzögerung in SelectionChanged zu schreiben, und ich habe das Gefühl, dass mein Code plötzlich viel eleganter wird.
  6. Ändern Sie den Ausdruck von _headerAnimation:

Hinweis: Max, Min und Clamp sind integrierte Funktionen in der Ausdrucksanimation. Weitere Informationen finden Sie im Anhang .

Machen Sie den Test noch einmal und bestehen Sie ihn perfekt. Eine weitere Lücke wurde gefüllt. Nachdem ich eine Weile mit dieser Demo gespielt habe, habe ich immer noch das Gefühl, dass es beim Wechseln der Seiten nach links und rechts immer noch zu schwergängig ist, den Kopf nach oben und unten zu bewegen. Meine Idee ist, die Ausdrucksanimation des Kopfes im Complate-Ereignis zu starten, das die Kopfpositionsanimation anpasst. Machen wir es:

 _headerAnimation = _compositor.CreateExpressionAnimation(

创建一个关键帧动画,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的控件都可以放到这个这个页面使用。

滑动返回:

Das obige ist der detaillierte Inhalt vonEinführung in die Verwendung der Composition API zum Erreichen der Deckenhöhe in UWP (2). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn