Обновление по запросу с помощью модификаторов источника
В этой статье мы более подробно рассмотрим, как использовать функцию SourceModifier InteractionTracker и продемонстрировать ее использование путем создания пользовательского элемента управления извлечения и обновления.
Необходимые компоненты
Здесь предполагается, что вы знакомы с понятиями, описанными в следующих статьях:
- Анимации на основе входных данных
- Пользовательские операции с помощью InteractionTracker
- Анимации на основе реляционных данных
Что такое SourceModifier и почему они полезны?
Как и инерционныеmodifierы, SourceModifiers дают более точный контроль над движением InteractionTracker. Но в отличие от инерционныхmodifiers, определяющих движение после того, как InteractionTracker входит в инерцию, SourceModifiers определяет движение, в то время как InteractionTracker по-прежнему находится в состоянии взаимодействия. В таких случаях требуется другой опыт, чем традиционный "придерживаться пальца".
Классическим примером этого является процесс обновления по запросу - когда пользователь извлекает список, чтобы обновить содержимое и списки сдвигается на той же скорости, что и палец и останавливается после определенного расстояния, движение будет чувствовать себя резко и механическим. Более естественный опыт будет представить чувство сопротивления, пока пользователь активно взаимодействует со списком. Этот маленький нюанс помогает сделать общий интерфейс пользователя взаимодействия со списком более динамичным и привлекательным. В разделе "Пример" мы подробно рассмотрим, как создать это.
Существует 2 типа модификаторов источника:
- DeltaPosition — это разность между текущей позицией кадра и предыдущей позицией кадра пальца во время взаимодействия сенсорного сдвига. Этот модификатор источника позволяет изменить разностную позицию взаимодействия перед отправкой его для дальнейшей обработки. Это параметр типа Vector3, и разработчик может изменить любой из атрибутов позиции X или Y или Z, прежде чем передать его в InteractionTracker.
- DeltaScale — это разностная разница между текущим масштабом кадра и предыдущим масштабом кадра, который был применен во время сенсорного масштабирования. Этот модификатор источника позволяет изменить уровень масштабирования взаимодействия. Это атрибут типа с плавающей запятой, который разработчик может изменить перед передачей в InteractionTracker.
Когда InteractionTracker находится в состоянии взаимодействия, он оценивает каждый из модификаторов источника, назначенных ему, и определяет, применяются ли какие-либо из них. Это означает, что вы можете создать и назначить несколько модификаторов источника элементу InteractionTracker. Но при определении каждого необходимо сделать следующее:
- Определите условие — выражение, определяющее условную инструкцию при применении этого конкретного модификатора источника.
- Определите deltaPosition/DeltaScale — исходное модификаторное выражение, которое изменяет DeltaPosition или DeltaScale при выполнении указанного выше условия.
Пример
Теперь давайте рассмотрим, как использовать модификаторы источника для создания пользовательского интерфейса по запросу для обновления с помощью существующего элемента управления ListView XAML. Мы будем использовать холст в качестве панели обновления, которая будет размещена на вершине XAML ListView для создания этого интерфейса.
Для взаимодействия с конечным пользователем мы хотим создать эффект "сопротивления", так как пользователь активно сдвигает список (с сенсорным подключением) и перестает сдвигаться после того, как позиция выходит за пределы определенной точки.
Рабочий код для этого интерфейса можно найти в репозитории Windows UI Dev Labs на GitHub. Вот пошаговые инструкции по созданию этого опыта. В коде разметки XAML есть следующее:
<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>
Так как ListView (ThumbnailList
) — это элемент управления XAML, который уже прокручивается, прокрутка должна быть привязана к родительскому элементу (ContentPanel
) при достижении самого верхнего элемента и больше не может прокручиваться. (ContentPanel — это место, где будут применяться модификаторы источника.) Для этого необходимо задать scrollViewer.IsVerticalScrollChainingEnabled значение true в разметке ListView. Кроме того, необходимо задать режим цепочки в VisualInteractionSource на Always.
Необходимо задать обработчик PointerPressedEvent с параметром handledEventsToo значение true. Без этого параметра pointerPressedEvent не будет привязан к ContentPanel, так как элемент управления ListView помечает эти события как обработанные, и они не будут отправляться вверх по визуальной цепочке.
//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);
Теперь вы готовы связать это с Помощью InteractionTracker. Начните с настройки InteractionTracker, VisualInteractionSource и выражения, которые будут использовать позицию 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);
При настройке панель обновления выходит из окна просмотра в начальной позиции, и все, что пользователь видит, является listView, когда сдвига достигает ContentPanel, событие PointerPressed будет запущено, где вы попросите систему использовать InteractionTracker для управления взаимодействием.
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));
}
}
Примечание.
Если события обработки цепочки не требуются, добавление обработчика PointerPressedEvent можно выполнить непосредственно с помощью разметки XAML с помощью атрибута (PointerPressed="Window_PointerPressed"
).
Следующим шагом является настройка модификаторов источника. Для получения этого поведения вы будете использовать 2 модификатора источника; Сопротивление и остановка.
- Сопротивление — переместить DeltaPosition.Y на половину скорости, пока она не достигнет высоты 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;
- Остановка— остановка перемещения после всего обновленияPanel находится на экране.
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);
На этой схеме показана визуализация установки SourceModifiers.
Теперь с SourceModifiers вы заметите, когда сдвигаете ListView вниз и достигает самого верхнего элемента, панель обновления вытягивается в половину темпа сдвига, пока она не достигнет высоты RefreshPanel, а затем перестает двигаться.
В полном примере анимация ключевого кадра используется для спины значка во время взаимодействия на холсте RefreshPanel. Любое содержимое можно использовать в своем месте или использовать положение InteractionTracker для отдельной анимации.