Freigeben über


Aktualisierung durch Ziehen mit Quellmodifizierern

In diesem Artikel gehen wir eingehend darauf ein, wie Sie die Funktion SourceModifier eines InteractionTracker-Elements verwenden und deren Einsatz demonstrieren, indem Sie ein benutzerdefiniertes Pull-to-Refresh-Steuerelement erstellen.

Voraussetzungen

Hier wird davon ausgegangen, dass Sie mit den in den folgenden Artikeln erläuterten Konzepten vertraut sind:

Was ist ein SourceModifier und warum sind sie nützlich?

Wie InertiaModifiers bieten SourceModifiers Ihnen eine feinere Kornkontrolle über die Bewegung eines InteractionTrackers. Im Gegensatz zu InertiaModifiern, die die Bewegung definieren, nachdem InteractionTracker in die Inertia wechselt, definieren SourceModifier die Bewegung, während InteractionTracker sich noch im Interaktionszustand befindet. In diesen Fällen möchten Sie etwas anderes erleben als das traditionelle "am Finger kleben".

Ein klassisches Beispiel dafür ist das Pull-to-Refresh-Erlebnis – wenn der Nutzer die Liste zieht, um den Inhalt zu aktualisieren, bewegt sich die Liste mit der gleichen Geschwindigkeit wie der Finger und stoppt nach einer bestimmten Strecke, wirkt die Bewegung abrupt und mechanisch. Eine natürlichere Erfahrung wäre die Einführung eines Widerstandsgefühls, während der Benutzer aktiv mit der Liste interagiert. Diese kleine Nuance trägt dazu bei, die gesamte Benutzererfahrung beim Interagieren mit einer Liste dynamischer und ansprechender zu gestalten. Im Abschnitt "Beispiel" erfahren Sie mehr darüber, wie Sie dies erstellen.

Es gibt zwei Arten von Quellmodifizierern:

  • DeltaPosition – ist das Delta zwischen der aktuellen Frameposition und der vorherigen Frameposition des Fingers während der Touchverschiebungsinteraktion. Mit diesem Quellmodifizierer können Sie die Deltaposition der Interaktion ändern, bevor Sie sie zur weiteren Verarbeitung senden. Dies ist ein Vector3-Typparameter, und der Entwickler kann eine der X- oder Y- oder Z-Attribute der Position ändern, bevor sie an den InteractionTracker übergeben wird.
  • DeltaScale – ist das Delta zwischen der aktuellen Frameskala und der vorherigen Frameskala, die während der Touchzoominteraktion angewendet wurde. Mit diesem Quellmodifizierer können Sie den Zoomfaktor der Interaktion ändern. Dies ist ein Float-Typattribut, das der Entwickler ändern kann, bevor er es an den InteractionTracker übergibt.

Wenn InteractionTracker sich in seinem Interaktionszustand befindet, wertet er jeden der ihm zugewiesenen Quellmodifizierer aus und bestimmt, ob eine dieser Modifizierer zutrifft. Dies bedeutet, dass Sie mehrere Quellmodifizierer erstellen und einem InteractionTracker zuweisen können. Beim Definieren der einzelnen Schritte müssen Sie jedoch folgendes tun:

  1. Definieren Sie die Bedingung – ein Ausdruck, der die bedingte Anweisung definiert, wenn dieser spezifische Quellmodifizierer angewendet werden soll.
  2. Definieren Sie die DeltaPosition/DeltaScale – Der Quellmodifiziererausdruck, der die DeltaPosition oder DeltaScale ändert, wenn die oben definierte Bedingung erfüllt ist.

Beispiel

Sehen wir uns nun an, wie Sie mit Quellmodifizierern eine benutzerdefinierte Aktualisierungserfahrung mit Pull-to-Refresh mit einem vorhandenen WinUI XAML ListView-Steuerelement erstellen können. Wir werden eine Canvas als "Aktualisierungspanel" verwenden, die über einer XAML-ListView gestapelt wird, um dieses Interface zu gestalten.

Für die Endbenutzererfahrung möchten wir die Wirkung von "Widerstand" schaffen, da der Benutzer die Liste (mit Toucheingabe) aktiv verschiebt und die Verschiebung beenden soll, nachdem die Position einen bestimmten Punkt überschreitet.

Liste mit Pull-to-Refresh

Der funktionierende Code für diese Erfahrung finden Sie im Window UI Dev Labs-Repository auf GitHub. Dies ist eine Schritt-für-Schritt-Anleitung zur Erstellung dieses Erlebnisses. Im XAML-Markupcode haben Sie Folgendes:

<StackPanel Height="500" MaxHeight="500" x:Name="ContentPanel" HorizontalAlignment="Left" VerticalAlignment="Top" >
 <Canvas Width="400" Height="100" x:Name="RefreshPanel" >
<Image x:Name="FirstGear" Source="ms-appx:///Assets/Loading.png" Width="20" Height="20" Canvas.Left="200" Canvas.Top="70"/>
 </Canvas>
 <ListView x:Name="ThumbnailList"
 MaxWidth="400"
 Height="500"
ScrollViewer.VerticalScrollMode="Enabled" ScrollViewer.IsScrollInertiaEnabled="False" ScrollViewer.IsVerticalScrollChainingEnabled="True" >
 <ListView.ItemTemplate>
 ……
 </ListView.ItemTemplate>
 </ListView>
</StackPanel>

Da der ListView (ThumbnailList) ein XAML-Steuerelement ist, das bereits scrollt, muss der Bildlauf zum übergeordneten Element (ContentPanel) weitergegeben werden, wenn das oberste Element erreicht ist und nicht mehr gescrollt werden kann. (ContentPanel ist der Ort, an dem Sie die Quellmodifizierer anwenden.) Dazu müssen Sie ScrollViewer.IsVerticalScrollChainingEnabled im ListView-Markup auf "true " festlegen. Außerdem müssen Sie den Verkettungsmodus in VisualInteractionSource auf Always festlegen.

Sie müssen den PointerPressedEvent-Handler mit dem parameter handledEventsToo als true festlegen. Ohne diese Option wird das PointerPressed-Ereignis nicht mit dem ContentPanel verknüpft, da das ListView-Steuerelement diese Ereignisse als behandelt markiert und sie nicht in die visuelle Hierarchie weitergeleitet werden.

//The PointerPressed handler needs to be added using AddHandler method with the //handledEventsToo boolean set to "true"
//instead of the XAML element's "PointerPressed=Window_PointerPressed",
//because the list view needs to chain PointerPressed handled events as well.
ContentPanel.AddHandler(PointerPressedEvent, new PointerEventHandler( Window_PointerPressed), true);

Jetzt können Sie dies mit InteractionTracker verknüpfen. Richten Sie zunächst InteractionTracker, die VisualInteractionSource und den Ausdruck ein, der die Position von InteractionTracker nutzt.

// InteractionTracker and VisualInteractionSource setup.
_root = ElementCompositionPreview.GetElementVisual(Root);
_compositor = _root.Compositor;
_tracker = InteractionTracker.Create(_compositor);
_interactionSource = VisualInteractionSource.Create(_root);
_interactionSource.PositionYSourceMode = InteractionSourceMode.EnabledWithInertia;
_interactionSource.PositionYChainingMode = InteractionChainingMode.Always;
_tracker.InteractionSources.Add(_interactionSource);
float refreshPanelHeight = (float)RefreshPanel.ActualHeight;
_tracker.MaxPosition = new Vector3((float)Root.ActualWidth, 0, 0);
_tracker.MinPosition = new Vector3(-(float)Root.ActualWidth, -refreshPanelHeight, 0);

// Use the Tacker's Position (negated) to apply to the Offset of the Image.
// The -{refreshPanelHeight} is to hide the refresh panel
m_positionExpression = _compositor.CreateExpressionAnimation($"-tracker.Position.Y - {refreshPanelHeight} ");
m_positionExpression.SetReferenceParameter("tracker", _tracker);
_contentPanelVisual.StartAnimation("Offset.Y", m_positionExpression);

Bei dieser Konfiguration befindet sich der Aktualisierungsbereich zu Beginn außerhalb des Viewports. Zuerst sehen die Benutzer nur die ListView. Sobald die Verschiebung das ContentPanel erreicht, wird das PointerPressed-Ereignis ausgelöst. Dabei fordern Sie das System auf, den InteractionTracker zu verwenden, um die Benutzerinteraktion zu steuern.

private void Window_PointerPressed(object sender, PointerRoutedEventArgs e)
{
if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Touch) {
 // Tell the system to use the gestures from this pointer point (if it can).
 _interactionSource.TryRedirectForManipulation(e.GetCurrentPoint(null));
 }
}

Hinweis

Wenn das Verketten von Handled-Ereignissen nicht erforderlich ist, kann das Hinzufügen von PointerPressedEvent-Handlern mithilfe des Attributs (PointerPressed="Window_PointerPressed") direkt über XAML-Markup erfolgen.

Der nächste Schritt besteht darin, die Quellmodifizierer einzurichten. Sie verwenden zwei Quellmodifizierer, um dieses Verhalten zu erhalten. Widerstand und Stopp.

  • Widerstand – Bewegen Sie die DeltaPosition.Y bei der Hälfte der Geschwindigkeit, bis sie die Höhe des RefreshPanel erreicht.
CompositionConditionalValue resistanceModifier = CompositionConditionalValue.Create (_compositor);
ExpressionAnimation resistanceCondition = _compositor.CreateExpressionAnimation(
 $"-tracker.Position.Y < {pullToRefreshDistance}");
resistanceCondition.SetReferenceParameter("tracker", _tracker);
ExpressionAnimation resistanceAlternateValue = _compositor.CreateExpressionAnimation(
 "source.DeltaPosition.Y / 3");
resistanceAlternateValue.SetReferenceParameter("source", _interactionSource);
resistanceModifier.Condition = resistanceCondition;
resistanceModifier.Value = resistanceAlternateValue;
  • Stop – Stoppen Sie die Bewegung, nachdem sich das gesamte RefreshPanel auf dem Bildschirm befindet.
CompositionConditionalValue stoppingModifier = CompositionConditionalValue.Create (_compositor);
ExpressionAnimation stoppingCondition = _compositor.CreateExpressionAnimation(
 $"-tracker.Position.Y >= {pullToRefreshDistance}");
stoppingCondition.SetReferenceParameter("tracker", _tracker);
ExpressionAnimation stoppingAlternateValue = _compositor.CreateExpressionAnimation("0");
stoppingModifier.Condition = stoppingCondition;
stoppingModifier.Value = stoppingAlternateValue;
Now add the 2 source modifiers to the InteractionTracker.
List<CompositionConditionalValue> modifierList = new List<CompositionConditionalValue>()
{ resistanceModifier, stoppingModifier };
_interactionSource.ConfigureDeltaPositionYModifiers(modifierList);

Dieses Diagramm gibt eine Visualisierung des SourceModifiers-Setups.

Schwenkdiagramm

Jetzt mit den SourceModifiers werden Sie feststellen, dass beim Herunterscrollen des ListView und Erreichen des obersten Elements das Aktualisierungsfeld mit halber Geschwindigkeit der Scrollbewegung nach unten gezogen wird, bis es die RefreshPanel-Höhe erreicht und sich dann nicht weiter bewegt.

Im vollständigen Beispiel wird eine Keyframeanimation verwendet, um während der Interaktion im RefreshPanel-Zeichenbereich ein Symbol zu drehen. Jeder Inhalt kann an seinem Ort verwendet werden oder die Position von InteractionTracker nutzen, um diese Animation separat zu steuern.