ABAP Netweaver, Hybris Commerce和SAP 云平臺的登錄認證
658
2025-03-31
概述
New UWP Community Toolkit? V6.0 的版本發(fā)布日志中提到了 Carousel 的調整,本篇我們結合代碼詳細講解? Carousel?的實現。
Carousel 是一種傳送帶形態(tài)的控件,在圖片展示類的應用中有非常多的應用,它擁有很好的流暢度,可以做很多的自定義,并集成了鼠標,觸摸板,鍵盤等的操作。我們來看一下官方的介紹和官網示例中的展示:
The?Carousel?control provides a new control, inherited from the?ItemsControl, representing a nice and smooth carousel.
This control lets you specify a lot of properties for a flexible layouting.
The?Carousel?control works fine with mouse, touch, mouse and keyboard as well.
Source:?https://github.com/Microsoft/UWPCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls/Carousel
Doc:?https://docs.microsoft.com/zh-cn/windows/uwpcommunitytoolkit/controls/carousel
Namespace:?Microsoft.Toolkit.Uwp.UI.Controls;?Nuget:?Microsoft.Toolkit.Uwp.UI.Controls;
開發(fā)過程
代碼分析
先來看看?Carousel 的類結構組成:
Carousel.cs - Carousel 控件類,繼承自 ItemsControl
Carousel.xaml -?Carousel 的樣式文件,包含了?Carousel,CarouselItem,CarouselPanel 的樣式
CarouselItem.cs -?CarouselItem 是?Carousel 控件的列表中的選擇器 ItemTemplate
CarouselPanel.cs -?CarouselPanel 是?Carousel 控件的 ItemPanelTemplate
下面來看一下幾個主要類中的主要代碼實現,因為篇幅關系,我們只摘錄部分關鍵代碼實現:
1. Carousel.cs
在具體分析代碼前,我們先看看 Carousel 類的組成:
可以看到,作為一個集合類控件,Carousel 也注冊了?SelectedItem 和?SelectedIndex 依賴屬性,并且因為控件可以控制元素的深度,旋轉角度,動畫時長和類型,列表方向等,注冊了?TransitionDuration,ItemDepth,EasingFunction,ItemMargin,ItemRotationX,Orientation 等依賴屬性。而部分依賴屬性的?PropertyChanged 事件由?OnCarouselPropertyChanged(d, e) 來實現;
下面來看一下 Carousel 類的構造方法:
構造方法中,首先設置了樣式,Tab 導航模式;定義了鼠標滾輪,鼠標點擊和鍵盤事件,并注冊了數據源變化事件來得到正確的 SelectedItem 和 SelectedIndex。
public?Carousel() {????//?Set?style ????DefaultStyleKey?=?typeof(Carousel); ????SetValue(AutomationProperties.NameProperty,?"Carousel"); ????IsHitTestVisible?=?true; ????IsTabStop?=?false; ????TabNavigation?=?KeyboardNavigationMode.Once;????//?Events?registered ????PointerWheelChanged?+=?OnPointerWheelChanged; ????PointerReleased?+=?CarouselControl_PointerReleased; ????KeyDown?+=?Keyboard_KeyUp;????//?Register?ItemSource?changed?to?get?correct?SelectedItem?and?SelectedIndex ????RegisterPropertyChangedCallback(ItemsSourceProperty,?(d,?dp)?=>?{?...?}); }
在鍵盤按鍵抬起的事件處理中,分別對應 Down,Up,Right 和 Left 做了處理,我們只截取了 Down 的處理過程;可以看到,如果列表方向為縱向,則 Down 按鍵會觸發(fā) SelectedIndex++,也就是當前選擇項下移一位;對應其他三個按鍵也是類似的操作;OnPointerWheelChanged 的實現方式類似,這里不贅述;
private?void?Keyboard_KeyUp(object?sender,?KeyRoutedEventArgs?e) {????switch?(e.Key) ????{????????case?Windows.System.VirtualKey.Down:????????case?Windows.System.VirtualKey.GamepadDPadDown:????????case?Windows.System.VirtualKey.GamepadLeftThumbstickDown:????????????if?(Orientation?==?Orientation.Vertical) ????????????{????????????????if?(SelectedIndex?
接著看一下?PrepareContainerForItemOverride(element, item) 方法,它為 Item 設置了初始的 3D 旋轉的中心點,Item 變換的中心點;并根據當前選擇項確定 Item 是否被選中;
protected?override?void?PrepareContainerForItemOverride(DependencyObject?element,?object?item) {????base.PrepareContainerForItemOverride(element,?item);????var?carouselItem?=?(CarouselItem)element; ????carouselItem.Selected?+=?OnCarouselItemSelected; ????carouselItem.RenderTransformOrigin?=?new?Point(0.5,?0.5); ????carouselItem.IsTabStop?=?Items.IndexOf(item)?==?SelectedIndex; ????carouselItem.UseSystemFocusVisuals?=?true; ????PlaneProjection?planeProjection?=?new?PlaneProjection(); ????planeProjection.CenterOfRotationX?=?0.5; ????planeProjection.CenterOfRotationY?=?0.5; ????planeProjection.CenterOfRotationZ?=?0.5;????var?compositeTransform?=?new?CompositeTransform(); ????compositeTransform.CenterX?=?0.5; ????compositeTransform.CenterY?=?0.5; ????compositeTransform.CenterZ?=?0.5; ????carouselItem.Projection?=?planeProjection; ????carouselItem.RenderTransform?=?compositeTransform;????if?(item?==?SelectedItem) ????{ ????????carouselItem.IsSelected?=?true; ????} }
2.?Carousel.xaml
如上面類結構介紹時所說,Carousel.xaml 是?Carousel 控件的樣式文件;下面代碼中我把非關鍵部分用 ‘...’ 代替了,可以看到,主要是兩個部分的樣式:CarouselItem 和?Carousel,CarouselPanel 作為?Carousel 的 ItemsPanelTemplate;Carousel 控件的 easing mode 是 'EaseOut'。
...
3. CarouselItem.cs
在前面?Carousel.xaml 中我們看到了?CarouselItem 的樣式,有針對?VisualStateManager 的樣式狀態(tài),而?CarouselItem 類則定義了這些狀態(tài)變化事件對應的處理方法。分別有?OnIsSelectedChanged,OnPointerEntered,OnPointerExited 和?OnPointerPressed,在觸發(fā)這些狀態(tài)時,CarouselItem 會對應切換到那個狀態(tài)時的樣式。
public?CarouselItem() {????//?Set?style ????DefaultStyleKey?=?typeof(CarouselItem); ????RegisterPropertyChangedCallback(SelectorItem.IsSelectedProperty,?OnIsSelectedChanged); }protected?override?void?OnPointerEntered(PointerRoutedEventArgs?e)?{...}protected?override?void?OnPointerExited(PointerRoutedEventArgs?e)?{...}protected?override?void?OnPointerPressed(PointerRoutedEventArgs?e)?{...}internal?event?EventHandler?Selected;private?void?OnIsSelectedChanged(DependencyObject?sender,?DependencyProperty?dp) {????var?item?=?(CarouselItem)sender;????if?(item.IsSelected) ????{ ????????Selected?.Invoke(this,?EventArgs.Empty); ????????VisualStateManager.GoToState(item,?SelectedState,?true); ????}????else ????{ ????????VisualStateManager.GoToState(item,?NormalState,?true); ????} }
4.?CarouselPanel.cs
同樣在具體分析代碼前,我們先看看 CarouselPanel 類的組成:
CarouselPanel 類繼承自 Panel 類,可以看到它接收的事件響應,有?OnTapped,OnManipulationDelta 和?OnManipulationCompleted,分別對應點按,觸摸移動和移動結束的處理。其中:
OnTapped 的處理主要是根據當前控件的可視化范圍和尺寸,判斷點擊的點對應哪個元素被選中;
OnManipulationDelta 則是根據觸控操作的方向和量度等,決定 Item 的動畫幅度,動畫速度和每個元素變換狀態(tài),以及選中元素的變化;
OnManipulationCompleted 則是在觸控結束后,確定結束動畫,以及結束時應該選中那個元素;
UpdatePosition() 方法則是在?OnManipulationDelta 方法觸發(fā)到 first 或 last 元素時,需要重新設置動畫;
GetProjectionFromManipulation(sender, e) 則是在?OnManipulationDelta 方法中,根據當前觸控的手勢,決定當前 Item 的 Projection;
GetProjectionFromSelectedIndex(i) 是根據當前選中的索引,來取得?Item 的 Projection;
ApplyProjection(element, proj, storyboard) 是應用獲取到的 Projection,包括旋轉,變換等動畫;
而因為?CarouselPanel 類繼承自 Panel 類,所以它也重寫了?MeasureOverride(availableSize) 和?ArrangeOverride(finalSize) 方法:
MeasureOverride(availableSize) 方法的實現中,主要是根據寬度和高度是否設置為無限值,如果是,且方向和元素排列順序一致,則尺寸為當前方向三個元素的寬度,然后把計算后的尺寸傳出去;
protected?override?Size?MeasureOverride(Size?availableSize) {????var?containerWidth?=?0d;????var?containerHeight?=?0d;????foreach?(FrameworkElement?container?in?Children) ????{ ????????container.Measure(availableSize);????????//?get?containerWidth?and?containerHeight?for?max????}????var?width?=?0d;????var?height?=?0d;????//?It's?a?Auto?size,?so?we?define?the?size?should?be?3?items ????if?(double.IsInfinity(availableSize.Width)) ????{ ????????width?=?Carousel.Orientation?==?Orientation.Horizontal???containerWidth?*?(Children.Count?>?3???3?:?Children.Count)?:?containerWidth; ????}????else ????{ ????????width?=?availableSize.Width; ????}????//?It's?a?Auto?size,?so?we?define?the?size?should?be?3?items ????if?(double.IsInfinity(availableSize.Height)) ????{ ????????height?=?Carousel.Orientation?==?Orientation.Vertical???containerHeight?*?(Children.Count?>?3???3?:?Children.Count)?:?containerHeight; ????}????else ????{ ????????height?=?availableSize.Height; ????} ????Clip?=?new?RectangleGeometry?{?Rect?=?new?Rect(0,?0,?width,?height)?};????return?new?Size(width,?height); }
ArrangeOverride(finalSize) 方法則是排列元素的處理,因為?Carousel 控件有動畫處理,所以在排列時需要考慮到元素排列的動畫,以及 Zindex;
protected?override?Size?ArrangeOverride(Size?finalSize) {????double?centerLeft?=?0;????double?centerTop?=?0; ????Clip?=?new?RectangleGeometry?{?Rect?=?new?Rect(0,?0,?finalSize.Width,?finalSize.Height)?};????for?(int?i?=?0;?i?
調用示例
示例中我們實現了橫向的?Carousel 控件,作為一個圖片列表,可以看到當前選中的 Item 的 ZIndex 是最高的,向兩側依次降低,而在滑動過程中,伴隨著 3D 和變換的動畫,ZIndex 也會一起變化,而滑動結束時,選中項重新計算,每一項的 Project 也會重新計算。
總結
到這里我們就把 UWP Community Toolkit 中的 Carousel 控件的源代碼實現過程和簡單的調用示例講解完成了,希望能對大家更好的理解和使用這個控件有所幫助,讓你的圖片列表控件更加炫酷靈動。歡迎大家多多交流,謝謝!
最后,再跟大家安利一下 UWPCommunityToolkit 的官方微博:https://weibo.com/u/6506046490,?大家可以通過微博關注最新動態(tài)。
計算 注冊
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現本站中有涉嫌抄襲或描述失實的內容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現本站中有涉嫌抄襲或描述失實的內容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。