1. 程式人生 > >第二十一章:變換(五)

第二十一章:變換(五)

規模變換

VisualElement類定義名為Scale的屬性,您可以使用該屬性更改元素的呈現大小。 Scale屬性不會影響佈局(將在ButtonScaler程式中演示)。它不會影響元素的get-only Width和Height屬性,也不會影響包含Width和Height值的get-only Bounds屬性。對Scale屬性的更改不會導致觸發SizeChanged事件。
縮放影響渲染視覺元素的座標,但與TranslationX和TranslationY的方式完全不同。兩個轉換屬性將值新增到座標,而Scale屬性是乘法。 Scale的預設值為1.大於1的值會增加元素的大小。例如,值3使元素的大小為正常大小的三倍。小於1的值會減小尺寸。 Scale值為0是合法的,但會導致元素不可見。如果您正在使用Scale並且您的元素似乎已經消失,請檢查它是否以某種方式獲得Scale值為0。
小於0的值也是合法的,並且除了改變尺寸之外,還使元件旋轉180度。
您可以使用SimpleScaleDemo程式試驗縮放設定。 (該程式有一個簡單的字首,因為它不包括AnchorX和AnchorY屬性的影響,這將很快討論。)XAML類似於TranslationDemo程式:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="SimpleScaleDemo.SimpleScaleDemoPage">
    <StackLayout Padding="20, 10">
        <Frame x:Name="frame"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand"
               OutlineColor="Accent">
         
            <Label Text="TEXT"
                   FontSize="Large" />
        </Frame>
 
        <Slider x:Name="scaleSlider"
                Minimum="-10"
                Maximum="10"
                Value="{Binding Source={x:Reference frame},
                Path=Scale}" />
        <Label Text="{Binding Source={x:Reference scaleSlider},
                              Path=Value,
                              StringFormat='Scale = {0:F1}'}"
               HorizontalTextAlignment="Center" />
    </StackLayout>
</ContentPage>

這是在行動。 請注意Android手機上的負面比例設定:
2019_01_10_100634
在Windows 10移動顯示器上,框架已縮放到如此大,以至於您無法看到其左側和右側。
在現實生活中,您可能希望使用“縮放”在單擊“按鈕”時向用戶提供一些反饋。 按鈕可以短暫地擴大尺寸並再次恢復正常。 但是,Scale不是更改Button大小的唯一方法。 您還可以通過增加和減少FontSize屬性來更改Button大小。 但是,這兩種技術非常不同:Scale屬性不會影響佈局,但FontSize屬性會影響佈局。
ButtonScaler程式說明了這種差異。 XAML檔案由兩個夾在兩對BoxView元素之間的Button元素組成:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ButtonScaler.ButtonScalerPage">
    <StackLayout>
        <!-- "Animate Scale" Button between two BoxViews. -->
        <BoxView Color="Accent"
                 HeightRequest="4"
                 VerticalOptions="EndAndExpand" />
        <Button Text="Animate Scale"
                FontSize="Large"
                BorderWidth="1"
                HorizontalOptions="Center"
                Clicked="OnAnimateScaleClicked" />
        <BoxView Color="Accent"
                 HeightRequest="4"
                 VerticalOptions="StartAndExpand" />
 
        <!-- "Animate FontSize" Button between two BoxViews. -->
        <BoxView Color="Accent"
                 HeightRequest="4"
                 VerticalOptions="EndAndExpand" />
        <Button Text="Animate FontSize"
                FontSize="Large"
                BorderWidth="1"
                HorizontalOptions="Center"
                Clicked="OnAnimateFontSizeClicked" />
        <BoxView Color="Accent"
                 HeightRequest="4"
                 VerticalOptions="StartAndExpand" />
    </StackLayout>
</ContentPage>

這是頁面通常的樣子:
2019_01_10_101019
程式碼隱藏檔案實現了一種有點通用的動畫方法。 在某種意義上,它是通用的,引數包括兩個值,表示動畫的起始值和結束值。 這兩個值通常稱為從值和值到值。 動畫引數還包括動畫的持續時間和回撥方法。 回撥方法的引數是“from”值和“to”值之間的值,並且呼叫方法可以使用該值來執行實現動畫所需的任何操作。
但是,這種動畫方法並不完全一般化。 它實際上在動畫的前半部分計算從值到值的值,然後在動畫的後半部分計算從值到值的值。 這有時被稱為倒車動畫。
該方法稱為AnimateAndBack,它使用Task.Delay呼叫來調整動畫,並使用.NET Stopwach物件來確定已用時間:

public partial class ButtonScalerPage : ContentPage
{
    public ButtonScalerPage()
    {
        InitializeComponent();
    }
    void OnAnimateScaleClicked(object sender, EventArgs args)
    {
        Button button = (Button)sender;
        AnimateAndBack(1, 5, TimeSpan.FromSeconds(3), (double value) =>
            {
                button.Scale = value;
            });
    }
    void OnAnimateFontSizeClicked(object sender, EventArgs args)
    {
        Button button = (Button)sender;
        AnimateAndBack(button.FontSize, 5 * button.FontSize, 
                       TimeSpan.FromSeconds(3), (double value) =>
            {
                button.FontSize = value;
            });
    }
    async void AnimateAndBack(double fromValue, double toValue, 
                              TimeSpan duration, Action<double> callback)
    {
        Stopwatch stopWatch = new Stopwatch();
        double t = 0;
        stopWatch.Start();
        while (t < 1)
        {
            double tReversing = 2 * (t < 0.5 ? t : 1 - t);
            callback(fromValue + (toValue - fromValue) * tReversing);
            await Task.Delay(16);
            t = stopWatch.ElapsedMilliseconds / duration.TotalMilliseconds;
        }
        stopWatch.Stop();
        callback(fromValue);
    }
}