溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

UWP中如何使用Composition API實現吸頂

發布時間:2020-10-19 14:17:15 來源:億速云 閱讀:196 作者:小新 欄目:編程語言

小編給大家分享一下UWP中如何使用Composition API實現吸頂,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

先做一個簡單的頁面,頁面有一個Grid當Header,一個去掉了頭部的Pivot,Pivot內有三個ListView,ListView設置了和頁面Header高度一致的空白Header。

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

Pivot的模板太長在這兒就不寫了,需要的話,找個系統內置的畫筆資源按F12打開generic.xaml,然后搜索Pivot就是了,其他控件的模板也能通過這個方法獲取。

模板里修改這幾句就能去掉頭部:

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

然后是后臺代碼,這里還會用到上一篇的FindFirstChild方法,在這兒就不貼出來了。

老樣子,全局的_headerVisual,最好在Page的Loaded事件里初始化我們所需要的這些變量,我偷懶了,直接放到了下面的UpdateAnimation方法里。
然后我們寫一個UpdateAnimation方法,用來在PivotItem切換的時候更新動畫的參數。

先判斷下如果未選中頁就return,然后獲取到當前選中項的容器,再像上次一樣從容器里獲取ScrollViewer,不過這里有個坑,稍后再說。

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

然后在Pivot的SelectionChanged事件里更新動畫:

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

點下運行,上下滑一下,并沒有跟著動。左右切換一下之后,發現在第二次切換到PivotItem的時候就可以跟著動了,下斷看到第一次運行到"var _scrollviewer = FindFirstChild<ScrollViewer>(SelectionItem);"的時候_scrollviewer為null。想了好久才意識到,是不是控件沒有Loaded的問題,所以才取不到子控件?說改就改。

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

再次運行,跟著動了。但是還有個問題,在每次切換的時候,Header都會回歸原位一次。這又是一個坑。
猜想在切換PivotItem的時候,_manipulationPropertySet.Translation.Y會有一個瞬間變成0。我踩過的坑大家就不要再踩了。
嘗試更新動畫前先停止動畫。

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

運行,果然失敗了。
這時靈光一閃,動畫播放時需要時間的??!這個切換的動畫大概是五步:

  1. 觸發SelectionChanged;

  2. 頁面左移并且逐漸消失;

  3. 卸載頁面;

  4. 裝載新頁面;

  5. 頁面從右側移動到中心并且逐漸顯現。

第一步開始之前觸發了SelectionChanged,然后停止動畫,更新動畫,我表達式動畫都開始播放了,他的第一步還慢悠悠的沒有走完...
簡單,在SelectionChanged里加延時,就能解決(是嗎)Header歸位的問題(這里又埋下一個坑):

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

運行,很完美。然后在手機上試了一下,差點哭了。
對于點擊和觸摸兩種操作方式,切換頁時觸發事件和播放動畫的順序不一樣!
觸摸造成的切換頁,大概是如下幾步:

  1. 滑動造成頁面位移,松手后頁面左移并且逐漸消失

  2. 觸發SelectionChanged;

  3. 卸載頁面;

  4. 裝載新頁面;

  5. 頁面從右側移動到中心并且逐漸顯現。

可是頁面消失后_manipulationPropertySet.Translation.Y會有一瞬間變成0??!這時候的我真的是崩潰的,不過最后還是給我想出了解決方案。
_manipulationPropertySet.Translation.Y變成0的時候不理他不就好了,不能再機智。這樣也不需要SelectionChanged里寫延時了,感覺自己的代碼一下子變得優雅了很多呢。
修改_headerAnimation的表達式:

 _headerAnimation = _compositor.CreateExpressionAnimation(

注:其中max,min,clamp都是表達式動畫中內置的函數,相關的信息可以查看附錄。

再進行測試,完美通過,又填好一個坑。玩弄了這個Demo一會兒后,總覺得還有些不足,左右切換頁的時候,頭部上下移動太生硬了。我的設想是在調整頭部位置動畫的Complate事件里開始頭部的表達式動畫,說干咱就干:

var line = _compositor.CreateCubicBezierEasingFunction(new System.Numerics.Vector2(0, 0), new System.Numerics.Vector2(0.6f, 1));var MoveHeaderAnimation = _compositor.CreateScalarKeyFrameAnimation();
MoveHeaderAnimation.InsertExpressionKeyFrame(0f, "_headerVisual.Offset.Y", line);
MoveHeaderAnimation.InsertExpressionKeyFrame(1f, "_manipulationPropertySet.Translation.Y > -100f ? _manipulationPropertySet.Translation.Y: -100f", line);
MoveHeaderAnimation.SetReferenceParameter("_headerVisual", _headerVisual);
MoveHeaderAnimation.SetReferenceParameter("_manipulationPropertySet", _manipulationPropertySet);
MoveHeaderAnimation.DelayTime = TimeSpan.FromSeconds(0.18d);
MoveHeaderAnimation.Duration = TimeSpan.FromSeconds(0.1d);

創建一個關鍵幀動畫,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事件里,這時再運行一下,完美。

UWP中如何使用Composition API實現吸頂

其實還是有點小問題,比如Header沒有設置Clip,上下移動的時候有時會超出預期的范圍之類的,有時間我們會繼續討論,這篇已經足夠長,再長會嚇跑人的。
Demo已經放到Github,里面用到了一個寫的很糙的滑動返回控件,等忙過這段時間整理下代碼就開源,希望能有大牛指點一二。

Github:

總結一下,實現吸頂最核心的代碼就是獲取到ScrollViewer,不一定要是ListView的,明白了這一點,所有含有ScrollViewer的控件都可以放到這個這個頁面使用。

滑動返回:

UWP中如何使用Composition API實現吸頂

以上是UWP中如何使用Composition API實現吸頂的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女