Udostępnij za pośrednictwem


Przeciągnij, aby odświeżyć z modyfikatorami źródłowymi

W tym artykule dowiesz się, jak używać funkcji SourceModifier usługi InteractionTracker i demonstrować jej użycie, tworząc niestandardową kontrolkę ściągania do odświeżenia.

Wymagania wstępne

W tym miejscu przyjęto założenie, że znasz pojęcia omówione w następujących artykułach:

Co to jest SourceModifier i dlaczego jest przydatny?

Podobnie jak InertiaModifiers, SourceModifiers zapewniają precyzyjniejszą kontrolę nad ruchem elementu InteractionTracker. Ale w przeciwieństwie do InertiaModifiers, które definiują ruch po tym, jak InteractionTracker wchodzi w inercję, SourceModifiers definiują ruch, podczas gdy InteractionTracker jest nadal w stanie interakcji. W takich przypadkach chcesz uzyskać inne wrażenia niż tradycyjne "przyklejanie się do palca".

Klasycznym przykładem tego jest funkcja przeciągnięcia do odświeżenia — gdy użytkownik ciągnie listę, aby odświeżyć zawartość, a lista porusza się z taką samą prędkością jak palec i zatrzymuje się po określonej odległości, ruch wydaje się nagły i mechaniczny. Bardziej naturalne doświadczenie polegałoby na wprowadzeniu poczucia oporu, podczas gdy użytkownik aktywnie wchodzi w interakcję z listą. Ten mały niuans pomaga uczynić ogólne doświadczenie użytkownika końcowego podczas interakcji z listą bardziej dynamiczne i atrakcyjne. W sekcji "Przykład" omawiamy bardziej szczegółowo, jak zbudować to rozwiązanie.

Istnieją 2 typy modyfikatorów źródła:

  • DeltaPosition — jest różnicą między bieżącą pozycją ramki a poprzednią pozycją ramki palca podczas interakcji z patelnią dotykową. Ten modyfikator źródła umożliwia modyfikowanie położenia różnicowego interakcji przed wysłaniem go do dalszego przetwarzania. Jest to parametr typu Vector3, a deweloper może zmodyfikować dowolne atrybuty X lub Y lub Z pozycji przed przekazaniem go do elementu InteractionTracker.
  • DeltaScale — jest różnicą między bieżącą skalą ramek a poprzednią skalą ramek zastosowaną podczas interakcji z powiększeniem dotykowym. Ten modyfikator źródła umożliwia modyfikowanie poziomu powiększenia interakcji. Jest to atrybut typu zmiennoprzecinkowego, który deweloper może zmodyfikować przed przekazaniem go do InteractionTracker.

Gdy element InteractionTracker jest w stanie Interacting, ocenia każdy z przypisanych do niego modyfikatorów źródła i określa, czy którykolwiek z nich ma zastosowanie. Oznacza to, że można utworzyć i przypisać wiele modyfikatorów źródłowych do elementu InteractionTracker. Jednak podczas definiowania każdego z nich należy wykonać następujące czynności:

  1. Zdefiniuj warunek — wyrażenie, które definiuje instrukcję warunkową po zastosowaniu tego konkretnego modyfikatora źródła.
  2. Zdefiniuj wyrażenie deltaPosition/DeltaScale — modyfikator źródła, które zmienia wartość deltaPosition lub DeltaScale po spełnieniu powyższego zdefiniowanego warunku.

Przykład

Teraz przyjrzyjmy się, jak za pomocą modyfikatorów źródła utworzyć niestandardowe środowisko korzystające z gestu przeciągnięcia, aby odświeżyć istniejącą kontrolkę WinUI XAML ListView. Użyjemy Canvas jako "Panelu odświeżania", który zostanie umieszczony na XAML ListView w celu utworzenia tego środowiska.

W przypadku środowiska użytkownika końcowego chcemy utworzyć efekt "oporu", ponieważ użytkownik aktywnie przesuwa listę (z dotykiem) i przestaje przesuwać się po tym, jak pozycja wykracza poza określony punkt.

Lista z funkcją pull-to-refresh

Kod roboczy dla tego środowiska można znaleźć w repozytorium Windows UI Dev Labs w witrynie GitHub. Oto krok po kroku przewodnik po tworzeniu tego doświadczenia. W kodzie znaczników XAML masz następujące elementy:

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

Ponieważ element ListView (ThumbnailList) jest kontrolką XAML, która już obsługuje przewijanie, musisz połączyć przewijanie w górę z elementem nadrzędnym (ContentPanel), gdy osiągnie najwyższy element i nie może przewijać dalej. (ContentPanel to miejsce, w którym zostaną zastosowane modyfikatory źródła). Aby tak się stało, należy ustawić wartość ScrollViewer.IsVerticalScrollChainingEnabled na wartość true w znaczniku ListView. Należy również ustawić tryb tworzenia łańcucha w usłudze VisualInteractionSource na zawsze.

Należy ustawić procedurę obsługi PointerPressedEvent z parametrem handledEventsToo na wartość true. Bez tej opcji zdarzenie PointerPressedEvent nie będzie propagowane do ContentPanel, ponieważ kontrolka ListView oznaczy te zdarzenia jako obsłużone i nie zostaną przesłane w górę łańcucha wizualnego.

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

Teraz możesz powiązać to z funkcją InteractionTracker. Zacznij od skonfigurowania InteractionTracker, VisualInteractionSource oraz wyrażenia, które będzie korzystać z pozycji InteractionTracker.

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

Po skonfigurowaniu panel odświeżania jest poza obszarem wyświetlania w pozycji początkowej, a użytkownik widzi element listView. Gdy przesuwanie dociera do ContentPanel, zostanie wyzwolone zdarzenie PointerPressed, w którym poproś system o użycie InteractionTracker, aby sterować doświadczeniem manipulacji.

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

Uwaga / Notatka

Jeśli nie jest konieczne łączenie obsługiwanych zdarzeń, obsługę zdarzenia PointerPressedEvent można dodać bezpośrednio w znacznikach XAML, używając atrybutu (PointerPressed="Window_PointerPressed").

Następnym krokiem jest skonfigurowanie modyfikatorów źródłowych. Aby uzyskać to zachowanie, będziesz używać 2 modyfikatorów źródłowych; Opór i zatrzymanie.

  • Opór — przenieś deltaPosition.Y z połową prędkości, aż osiągnie wysokość RefreshPanel.
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;
  • Zatrzymaj – zatrzymaj przesuwanie po tym, jak cały panel RefreshPanel znajduje się na ekranie.
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);

Ten diagram przedstawia wizualizację konfiguracji sourceModifiers.

Diagram panoramowania

Teraz przy użyciu elementów SourceModifiers zauważysz, że podczas przesuwania widoku ListView w dół i docierania do najwyższego poziomu elementu, panel odświeżania jest ściągany w połowie tempa patelni, aż osiągnie wysokość RefreshPanel, a następnie przestanie się poruszać.

W pełnym przykładzie animacja klatki kluczowej służy do obracania ikony podczas interakcji w płótnie RefreshPanel. Dowolną zawartość można używać na jego miejsce lub wykorzystać pozycję InteractionTracker, aby oddzielnie prowadzić animację.