2017/5/15

Xamarin.Forms 教學系列文(十五.壹)互動元件 -- Slider & Stepper



學習目標
  • Slider & 詭異的最小最大值
  • Stepper

15 章主要在介紹 Xamarin 的互動元件 (輕鬆但繁瑣的一章啊...)

 Button 就屬於互動元件。

而本次要讓大家認識的有八種 ViewElement,分別列出對應的資料型態:

Data Type
Views
Double
Slider, Stepper
Boolean
Switch
String
Entry, Editor, SearchBar
DateTime
DatePicker, TimePicker

共會分成四個小節來介紹,先從 Slider 開始。


Slider

Xamarin.Forms Slider 是一個水平的拖曳條,擁有最小值(在左邊)和最大值(在右邊)
(Xamarin.Forms 無提供垂直的 Slider)

每個平台顯示的 Slider 都有些微的不同,但使用方式大致一樣,可以簡單的用拖曳的方式控制資料。

Slider 定義了三個為 double 的公用屬性,Minimum, Maximum Value,當 Value 改變時會去觸發 ValueChanged 事件。

以下是簡易的 Slider Demo Code,可以看到初始指標會在最左邊,拖曳時 Label 顯示值:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="SliderDemo.SliderDemoPage">
    
    <StackLayout Padding="10, 0">
        <Slider VerticalOptions="CenterAndExpand" 
                ValueChanged="OnSliderValueChanged" />
        
        <Label x:Name="label" 
               FontSize="Large"
               HorizontalOptions="Center" 
               VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>


注意:不要設定 Slider 的 HorizontalOptions ,除非你有設定 WidthRequest,不然 Slider 會變得非常小。
.cs:
    public partial class SliderDemoPage : ContentPage
    {
        public SliderDemoPage()
        {
            InitializeComponent();
        }
        void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
        {
            label.Text = String.Format("Slider = {0}", args.NewValue);
        }
    }

Args 定義了 OldValue NewValue 這兩個 double 屬性可以取得並使用。

但這不是取得 Slider 值的唯一方法,也可以像下面的程式碼:
        void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
        {
            Slider slider = (Slider)sender;
            label.Text = String.Format("Slider = {0}", slider.Value);
        }

當然也可以設定 Minimum 和 Maxmum 任何正負的數值來限制範圍,

像底下程式碼,Slider 的範圍就是 0 ~ 100:
<Slider ValueChanged="OnSliderValueChanged" 
  Maximum="100" 
  VerticalOptions="CenterAndExpand" />


But... 當你要設定 Slider 的範圍從 1~100 時,如下:
<Slider ValueChanged="OnSliderValueChanged" 
  Minimum="1" 
  Maximum="100" 
  VerticalOptions="CenterAndExpand" />

程式執行後,會拋出例外 ArgumentException : Value was an invalid value for Minimum,導致執行錯誤 (畫面一片空白啊)。

這也是我覺得很詭異的地方... 簡單的設定最小最大值,卻如此地不人性...


Slider 的詭異設定

上面的錯誤是因為物件在初始化時,會先將 Maximum 值預設為 1,等到程式碼開始逐行執行,遇到 Minimum 設定為 1

此時 Minimun == Maximun,系統就會發出例外錯誤,因為 Slider 有一個規則,Maximum 的值一定要大於 Minimun

邏輯上來說,解決這問題最簡單的方法就是先設定 Maximun:
<Slider ValueChanged="OnSliderValueChanged" 
  Maximum="100" 
  Minimum="1" 
  VerticalOptions="CenterAndExpand" />


But

執行後又會出現另一個錯誤,在 ValueChanged 內發生 NullReferenceException

這是因為當 Minimum 值設定為 1 時,會去觸發 ValueChanged 事件時,而 ValueChanged 內的 Label 還未被初始化,所以跳出例外錯誤。

不論如何,最簡單的解法就是將 ValueChanged 方法往後移:
<Slider Maximum="100" 
 Minimum="1" 
 ValueChanged="OnSliderValueChanged" 
 VerticalOptions="CenterAndExpand" />

開發者表示:乾。


Slider 其他範例

這邊來看一支利用 Slider 控制 Label 透明度的程式

畫面上有兩個 Label,在 Slider 拖曳時可以看到其透明度的更改:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="TextFade.TextFadePage"
             Padding="10, 0, 10, 20">

    <StackLayout>
        <AbsoluteLayout VerticalOptions="CenterAndExpand">
            <!-- Label 1 -->
            <Label x:Name="label1" 
                   Text="TEXT" 
                   FontSize="Large"
                   AbsoluteLayout.LayoutBounds="0, 0.5" 
                   AbsoluteLayout.LayoutFlags="PositionProportional" />

            <!-- Label 2 -->
            <Label x:Name="label2" 
                   Text="FADE"
                   FontSize="Large"
                   Opacity="0" 
                   AbsoluteLayout.LayoutBounds="0, 0.5"
                   AbsoluteLayout.LayoutFlags="PositionProportional" />

        </AbsoluteLayout>
        <Slider ValueChanged="OnSliderValueChanged" />
    </StackLayout>
</ContentPage>

.cs :
    public partial class TextFadePage : ContentPage
    {
        public TextFadePage()
        {
            InitializeComponent();
        }

        void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
        {
            AbsoluteLayout.SetLayoutBounds(label1,
                new Rectangle(args.NewValue, 0.5, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));

            AbsoluteLayout.SetLayoutBounds(label2,
                new Rectangle(args.NewValue, 0.5, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));

            //更改透明度
            label1.Opacity = 1 - args.NewValue;
            label2.Opacity = args.NewValue;
        }
    }



Stepper

Stepper 和 Slider 很像,有 Minimum 和 Maximum 可以設定,也有 ValueChanged 事件可以觸發。

而與 Slidder 不一樣的是 Increment 屬性 (每次的增量) 預設值為 1。

以下程式碼展示如何利用 Stepper 去更改 Button 的 border width:

*Oplatform 寫法已更改,請參考此篇文章
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="StepperDemo.StepperDemoPage">

    <StackLayout>
        <!--Button-->
        <Button x:Name="button" 
                Text=" Sample Button" 
                FontSize="Large"
                HorizontalOptions="Center" 
                VerticalOptions="CenterAndExpand">

            <Button.BackgroundColor>
                <OnPlatform x:TypeArguments="Color" 
                            Android="#404040" />
            </Button.BackgroundColor>
            <Button.BorderColor>
                <OnPlatform x:TypeArguments="Color" 
                            Android="#C0C0C0"
                            WinPhone="Black" />
            </Button.BorderColor>
        </Button>

        <StackLayout VerticalOptions="CenterAndExpand">
            <StackLayout Orientation="Horizontal"
                         HorizontalOptions="Center">
                <StackLayout.Resources>
                    <ResourceDictionary>
                        <Style TargetType="Label">
                            <Setter Property="FontSize"
                                    Value="Medium" />
                        </Style>
                    </ResourceDictionary>
                </StackLayout.Resources>
                <Label Text="Button Border Width =" />
                <Label x:Name="label" />
            </StackLayout>

            <!--Stepper-->
            <Stepper x:Name="stepper" 
                     Maximum="10" 
                     ValueChanged="OnStepperValueChanged"
                     HorizontalOptions="Center" />
        </StackLayout>
    </StackLayout>
</ContentPage>

.cs :
    public partial class StepperDemoPage : ContentPage
    {
        public StepperDemoPage()
        {
            InitializeComponent();
            // Initialize display. 
            OnStepperValueChanged(stepper, null);
        }

        void OnStepperValueChanged(object sender, ValueChangedEventArgs args)
        {
            Stepper stepper = (Stepper)sender;
            button.BorderWidth = stepper.Value;
            label.Text = stepper.Value.ToString("F0");
        }
    }


執行結果:







沒有留言:

張貼留言