XAML in Xamarin.Forms 基礎篇 電子書

XAML in Xamarin.Forms 基礎篇 電子書
XAML in Xamarin.Forms 基礎篇 電子書

Xamarin.Forms 快速入門 電子書

Xamarin.Forms 快速入門 電子書
Xamarin.Forms 快速入門 電子書

2016/07/12

Xamarin.Forms 手勢操作

手勢操作

在行動裝置中,手勢操作功能相當的重要,例如,使用手指點擊螢幕、兩指縮放、拖拉等等;使用Xamarin.Forms GestureRecognizer 類別,可以任您應用程式中的使用者介面控制項,具有處理點擊、雙指縮放、平移這些手勢操控的偵測與處理能力。
另外,對於像是 ImageLabel 這類控制項,Xamarin.Forms 並沒有提供類似按鈕的 Click 的事件,因次,若想要偵測到使用者有點選這些控制項,就需要使用到這篇章節所提到手勢操作功能。

建立標籤式的樣板式頁面方案

  1. 首先,開啟您的 Visual Studio 2015
  2. 接著透過 Visual Studio 2015 功能表,選擇這些項目 檔案 > 新增 > 專案 準備新增一個專案。
  3. 接著,Visual Studio 2015 會顯示 新增專案 對話窗,請在這個對話窗上,進行選擇 Visual C# >Cross-Platform > Blank Xaml App (Xamarin.Forms Portable)
  4. 接著,在最下方的 名稱 文字輸入盒處,輸入 XFGestures 這個名稱,最後使用滑鼠右擊右下方的 確定 按鈕。
  5. 當專案建立到一半,若您的開發環境還沒有建置與設定完成 Mac 電腦與 Xamarin Studio for Mac 系統,此時會看到 Xamarin Mac Agent Instructions 對話窗出現,這個對話窗是要提醒您進行與 Mac 電腦連線設定,這是因為,您無法在 Windows 作業系統進行 iOS 相關應用程式的建立與設計工作,而是需要透過 Mac 電腦安裝的 XCode 來協助您完成這些 iOS 應用程式的建立工作。不過,這不影響您繼續開發 Xamarin.Forms 的應用程式,只不過此時,您無法編譯與執行 iOS 的應用程式。
  6. 接著會看到 新的通用Windows專案 對話視窗,此時,您只需要按下 確定 按鈕即可,此時,專案精靈會繼續完成相關平台的專案建立工作。
  7. 最後,整個新的 Xamarin.Forms 專案就建立完成了。

準備所需要的圖片資源

這個練習專案需要用到一些圖片檔案,因此,您需要進行將這些圖片檔案複製到適當的位置,這些檔案位於https://github.com/vulcanlee/Xamarin.Forms.StepByStep/tree/master/Images/XFGestures

Android 平台

當圖片檔案下載下來之後,請使用檔案總管全選這些圖片檔案,並且拖拉到 Android 專案 XFGestures.Droid > Resources > drawable 目錄內,如下圖所示: 當這些圖片檔案拖拉到 Android 專案內的 drawable 資料夾內,這些圖片檔案的建置動作必須是 AndroidResource 複製圖片檔案到Android專案內

iOS 平台

請使用檔案總管全選這些圖片檔案,並且拖拉到 iOS 專案 XFGestures.iOS > Resources 目錄內,這些圖片檔案的建置動作必須是 BundleResource 複製圖片檔案到iOS專案內

升級到 Xamarin.Forms 2.3 板以上

這個範例專案內,會使用到 Margin 這個屬性,因此,需要升級 Xamarin.Forms 套件到2.3以上版本
  • 滑鼠右擊方案 XFGestures,選擇 管理方案的 NuGet 套件
  • 點選 已安裝 > Xamarin.Forms 套件 > 選擇所有專案 > 選擇最新版本編號 > 安裝
    升級方案內所有專案的Xamarin.Forms套件

進階應用 Font Awesome

這個範例專案內將會使用到 Font Awesome 字體,您需要分別設定 Android 與 iOS 類型專案,使其可以使用 Font Awesome 字體圖示。

下載 Font Awesome 字型檔案與安裝

先打開瀏覽器,連線https://github.com/vulcanlee/Xamarin.Forms.StepByStep/tree/master/Font%20Awesome 會看到 Font Awesome 字型檔案 fontawesome.ttf ,點擊該連結,下載 Font Awesome的檔案到您本機磁碟機某個目錄上。
使用檔案總管,將剛剛下載的這個 fontawesome.ttf 檔案,拖拉到 XFGestures.Droid 專案內的 Assets資料夾內。
fontawesome.ttf在Assets內
使用檔案總管,將這個 fontawesome.ttf 檔案,拖拉到 XFGestures.iOS 專案內的 Resources 資料夾內。
fontawesome.ttf在Resources內
想要在 Android 與 iOS 系統內使用 Font Awesome 提供的字體圖示,做法並不一樣,底下分別就兩個平台的做法進行說明。

Android 專案部分

請在 XFGestures.Droid 專案內,建立一個名為 Renderers 資料夾,並且在此資料夾內,建立一個類別檔案 AwesomeRenderer.cs。該類別檔案的內容如下所示:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Xamarin.Forms.Platform.Android;
using Xamarin.Forms;
using XFGestures.Droid;
using Android.Graphics;
using XFGestures.Droid.Renders;

[assembly: ExportRenderer(typeof(Label), typeof(AwesomeLabelRenderer))]
[assembly: ExportRenderer(typeof(Xamarin.Forms.Button), typeof(AwesomeButtonRenderer))]
namespace XFGestures.Droid.Renders
{
    public class AwesomeLabelRenderer : LabelRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            AwesomeUtil.CheckAndSetTypeFace(Control);
        }
    }

    public class AwesomeButtonRenderer : ButtonRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
        {
            base.OnElementChanged(e);

            AwesomeUtil.CheckAndSetTypeFace(Control);
        }
    }

    internal static class AwesomeUtil
    {
        public static void CheckAndSetTypeFace(TextView view)
        {
            if (view.Text.Length == 0) return;
            var text = view.Text;
            if (text.Length > 1 || text[0] < 0xf000)
            {
                return;
            }

            var font = Typeface.CreateFromAsset(Xamarin.Forms.Forms.Context.ApplicationContext.Assets, "fontawesome.ttf");
            view.Typeface = font;
        }
    }
}

iOS 專案部分

在 XamarinLogin.iOS 專案中,使用滑鼠右擊 Info.plist 檔案,在彈出功能表中,選擇 開啟方式,再出現 開啟方式 - Info.plist 對話窗後,請選擇 XML(文字)編輯器,接著,點擊 確定 按鈕。
此時,Visual Studio 會開啟 XML 編輯器,請參考下兩圖,加入底下 XML 宣告到 Info.plist 檔案內。
    <key>UIAppFonts</key>
    <array>
      <string>fontawesome.ttf</string>
    </array>  </dict>
  • Info.plist尚未修改前的內容截圖
    Info.plist尚未修改前
  • Info.plist尚未修改後的內容截圖
    Info.plist尚未修改後

開始進行 核心PCL 頁面設計

App.xaml.cs

在 App 類別的建構式內,使用底下程式碼置換。
這個應用程式將會使用 TabbedPage 標籤頁面作為第一個呈現的頁面,在這個頁面中,一共加入了三個子標籤頁面 TapInsideImageTapInsideFrameTapInsideFrameXaml。在這個範例程式中,將會說明如何使用點擊的手勢操作功能。
  • TapInsideImage
    這個頁面展示了如何透過 C# 程式碼,在一個 Image 控制項中,綁定一個手勢物件,當使用者對於這個圖片有點選動作產生時候,就會觸發相對應的事件。
  • TapInsideFrame
    這個頁面展示了如何透過 C# 程式碼,在一個 Frame 框架中,綁定一個手勢物件,當使用者對於這個框架有點選動作產生時候,就會觸發相對應的事件。
  • TapInsideFrameXaml
    這個頁面展示了如何透過 XAML 的宣告定義與 ICommand 的資料綁定功能,針對 Image 與 Label 這兩個控制項,提供手勢操控的處理,其中,透過了 NumberOfTapsRequired 這個屬性設定,可以控制手勢點選是單點,還是雙擊。

App.xaml.cs

        public App()
        {
            InitializeComponent();

            //MainPage = new XFGestures.MainPage();

            var tabs = new TabbedPage();

            // demonstrates an Image tap (and changing the image)
            tabs.Children.Add(new TapInsideImage { Title = "圖片手勢", Icon = "csharp.png" });

            // demonstrates adding GestureRecognizer to a Frame
            tabs.Children.Add(new TapInsideFrame { Title = "框架手勢", Icon = "csharp.png" });

            // demonstrates using Xaml, Command and databinding
            tabs.Children.Add(new TapInsideFrameXaml { Title = "Xaml手勢", Icon = "xaml.png" });

            MainPage = tabs;
        }

TapInsideImage

請在核心PCL專案,使用滑鼠右擊 XFGestures,在彈出功能表中,點選 加入 > 類別,當出現 加入新項目 - XFGestures 對話視窗後,點選 Visual C# > 介面,接著在最下方的 名稱 欄位文字輸入盒中,輸入TapInsideImage
接著,請將底下 C# 程式碼置換剛剛產生的介面程式碼。

TapInsideImage.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace XFGestures
{
    public class TapInsideImage : ContentPage
    {
        int tapCount;
        readonly Label label;

        public TapInsideImage()
        {
            var image = new Image
            {
                Source = "tapped.jpg",

                HorizontalOptions = LayoutOptions.Center,
                VerticalOptions = LayoutOptions.CenterAndExpand,
            };
            var foo = new StackLayout();


            var tapGestureRecognizer = new TapGestureRecognizer();
            //            tapGestureRecognizer.NumberOfTapsRequired = 2; // double-tap
            tapGestureRecognizer.Tapped += OnTapGestureRecognizerTapped;
            image.GestureRecognizers.Add(tapGestureRecognizer);


            label = new Label
            {
                Text = "點擊圖片!",
                FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
                HorizontalOptions = LayoutOptions.Center,
                VerticalOptions = LayoutOptions.CenterAndExpand
            };

            label.GestureRecognizers.Add(tapGestureRecognizer);

            Content = new StackLayout
            {
                Padding = new Thickness(20, 100),

                Children =
                {
                    image,
                    label
                }
            };
        }

        void OnTapGestureRecognizerTapped(object sender, EventArgs args)
        {
            tapCount++;
            label.Text = String.Format("已經觸發 {0} 次點擊(Tap)!",
                tapCount,
                tapCount == 1 ? "" : "s");

            var imageSender = (Image)sender;

            // watch the monkey go from color to black&white!
            if (tapCount % 2 == 0)
            {
                imageSender.Source = "tapped.jpg";
            }
            else
            {
                imageSender.Source = "tapped_bw.jpg";
            }
            Debug.WriteLine("image tapped: " + ((FileImageSource)imageSender.Source).File);
        }
    }
}

TapInsideFrame

請在核心PCL專案,使用滑鼠右擊 XFGestures,在彈出功能表中,點選 加入 > 類別,當出現 加入新項目 - XFGestures 對話視窗後,點選 Visual C# > 介面,接著在最下方的 名稱 欄位文字輸入盒中,輸入TapInsideFrame
接著,請將底下 C# 程式碼置換剛剛產生的介面程式碼。

TapInsideFrame.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace XFGestures
{
    public class TapInsideFrame : ContentPage
    {
        int tapCount;
        readonly Label label;

        public TapInsideFrame()
        {
            var frame = new Frame
            {
                OutlineColor = Color.Accent,
                BackgroundColor = Color.Transparent,
                Padding = new Thickness(20, 100),
                HorizontalOptions = LayoutOptions.Center,
                VerticalOptions = LayoutOptions.CenterAndExpand,
                Content = new Label
                {
                    Text = "點擊(Tap) 框架內部",
                    FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label))
                }
            };


            var tapGestureRecognizer =
                new TapGestureRecognizer();
            //tapGestureRecognizer.NumberOfTapsRequired = 2; // double-tap
            tapGestureRecognizer.Tapped += OnTapGestureRecognizerTapped;
            frame.GestureRecognizers.Add(tapGestureRecognizer);


            label = new Label
            {
                Text = " ",
                FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
                HorizontalOptions = LayoutOptions.Center,
                VerticalOptions = LayoutOptions.CenterAndExpand
            };

            Content = new StackLayout
            {
                Children =
                {
                    frame,
                    label
                }
            };
        }

        void OnTapGestureRecognizerTapped(object sender, EventArgs args)
        {
            tapCount++;
            label.Text = String.Format("已經觸發 {0} 次點擊(Tap)!",
                tapCount,
                tapCount == 1 ? "" : "s");
        }
    }
}

TapInsideFrameXaml

請在核心PCL專案,使用滑鼠右擊 XFGestures,在彈出功能表中,點選 加入 > 新增項目,當出現 加入新項目 - XFGestures 對話視窗後,點選 Visual C# > Cross-Platform > Forms Xaml Page,接著在最下方的 名稱 欄位文字輸入盒中,輸入 TapInsideFrameXaml
由於這裡產生的是一個 Forms Xaml Page 項目,因此,需要定義 XAML 內容與 code-behind 的 C# 程式碼。 接著,請將底下 C# 程式碼置換剛剛產生的介面程式碼。

TapInsideFrameXaml.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XFGestures.TapInsideFrameXaml">
  <StackLayout>

    <Image Source="tapped.jpg" HeightRequest="150" WidthRequest="150">
      <Image.GestureRecognizers>
        <!-- Using the Command property works fine -->
        <TapGestureRecognizer
            Command="{Binding TapCommand}"
            CommandParameter="Image1" />
      </Image.GestureRecognizers>

    </Image>

    <Label Text="點擊(Tap)猴子圖片或者底下社群字體" Font="Large"
           HorizontalOptions="Center">
    </Label>

    <StackLayout
      Orientation="Horizontal"
      HorizontalOptions="Center">
       <Label Text="&#xf230;"
           FontSize="60"
           FontFamily="FontAwesome"
           Margin="0,0,40,0"
           >
        <Label.GestureRecognizers>
          <TapGestureRecognizer
              NumberOfTapsRequired="1"
              Command="{Binding TapCommand}"
              CommandParameter="Frame1" />
        </Label.GestureRecognizers>
      </Label>
      <Label Text="&#xf17a;"
           FontSize="60"
           FontFamily="FontAwesome"
           Margin="0,0,40,0"
           >
        <Label.GestureRecognizers>
          <TapGestureRecognizer
              NumberOfTapsRequired="2"
              Command="{Binding TapCommand}"
              CommandParameter="Frame1" />
        </Label.GestureRecognizers>
      </Label>
      <Label Text="&#xf0d4;"
           FontSize="60"
           FontFamily="FontAwesome"
           >
        <Label.GestureRecognizers>
          <TapGestureRecognizer
              NumberOfTapsRequired="2"
              Command="{Binding TapCommand}"
              CommandParameter="Frame1" />
        </Label.GestureRecognizers>
      </Label>
   </StackLayout>
    <Label x:Name="label"
           Text="{Binding NumberOfTapsTapped}"
           Font="Large"
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand"
            />

  </StackLayout>
</ContentPage>
在建構式內,將這個頁面的 BindingContext 屬性,設定為 TapViewModel 這個 ViewModel 所產生的物件,這樣,在整個頁面內,就可以透過資料綁定的方式,取得 ViewModel 內的資料。

TapInsideFrameXaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace XFGestures
{
    public partial class TapInsideFrameXaml : ContentPage
    {
        public TapInsideFrameXaml()
        {
            InitializeComponent();

            // The TapViewModel contains the TapCommand which is wired up in Xaml
            BindingContext = new TapViewModel();
        }
    }
}

TapViewModel

請在核心PCL專案,使用滑鼠右擊 XFGestures,在彈出功能表中,點選 加入 > 類別,當出現 加入新項目 - XFGestures 對話視窗後,點選 Visual C# > 介面,接著在最下方的 名稱 欄位文字輸入盒中,輸入TapViewModel
接著,請將底下 C# 程式碼置換剛剛產生的介面程式碼。

TapViewModel.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;

namespace XFGestures
{
    /// <summary>
    /// ViewModel to demonstrate binding to a Command from a GestureRecognizer
    /// </summary>
    /// <remarks>
    /// View models can be used regardless of whether the UI is build in code or with Xaml.
    /// In this example the view model is referenced by a Xaml page, but the same bindings
    /// can be done in C#.
    /// </remarks>
    public class TapViewModel : INotifyPropertyChanged
    {
        ICommand tapCommand;
        int taps = 0;
        string numberOfTapsTapped;

        public TapViewModel()
        {
            // configure the TapCommand with a method
            tapCommand = new Command(OnTapped);
        }

        /// <summary>
        /// Expose the TapCommand via a property so that Xaml can bind to it
        /// </summary>
        public ICommand TapCommand
        {
            get { return tapCommand; }
        }

        /// <summary>
        /// Called whenever TapCommand is executed (because it was wired up in the constructor)
        /// </summary>
        void OnTapped(object s)
        {
            taps++;
            Debug.WriteLine("parameter: " + s);
            NumberOfTapsTapped = String.Format("已經觸發 {0} 次點擊(Tap)",
                taps,
                taps == 1 ? "" : "s");
        }

        /// <summary>
        /// Display string that is bound to a Label on the page
        /// </summary>
        public string NumberOfTapsTapped
        {
            get { return numberOfTapsTapped; }
            set
            {
                if (numberOfTapsTapped == value)
                    return;
                numberOfTapsTapped = value;
                OnPropertyChanged();
            }
        }

        #region INotifyPropertyChanged 
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion
    }
}

實際執行畫面

請再度重新執行,這個時候應用程式可以順利正常運行,螢幕截圖如下:
Android_XFGestures_1
Android_XFGestures_2
Android_XFGestures_4
Android_XFGestures_3

佈署注意事項

iOS 執行結果

請在方案總管內,滑鼠右擊 XFGestures.iOS 專案,選擇 設定為起始專案,接著按下 F5 開始執行。
iOS_XFGestures_1
iOS_XFGestures_2
iOS_XFGestures_4
iOS_XFGestures_3

佈署注意事項

參考

沒有留言:

張貼留言