XAML in Xamarin.Forms 基礎篇 電子書

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

Xamarin.Forms 快速入門 電子書

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

2017/01/08

Xamarin FAQ 1-06 : 那些 Android SDK 套件需要使用 Android SDK Manager 來安裝呢?

問題

很多人在進行 Android 專案開發的時候,往往會遇到一些奇怪的問題,甚至在要建置專案的時候,就會產生相當多的錯誤訊息,如 Android.Support.V4.Widget.DrawerLayout.AddDrawerListener;其實,很多時候,這是您的 Android SDK Manager 並沒有安裝與設定完全。

解答

  • 請先安裝 Java JDK 1.8 以上的版本到您的電腦上
  • 在 Visual Studio 2015 中,點選功能表 工具 > 選項 > Xamarin > Android Settings
  • 請設定 Java Development Kit Location 這個欄位,指向您 JDK 1.8 版本安裝的位置,例如: C:\Program Files\Java\jdk1.8.0_102
  • 在 Visual Studio 2015 中,點選功能表 工具 > Android > Android SDK Manager
  • 在 Android SDK Manager 請確認底下項目已經確實有安裝且安裝到最新版本
    • Tools > Android SDK Tools
    • Tools > Android SDK Platform-tools
    • Tools > Android SDK Builder-tools ( 這裡僅需要安裝最新版本,舊的本版可以移除,不會有影響)
    • 每個 Android 版本內的 SDK Platform 項目要安裝 ( 在這裡建議把 Android 4.4.2 (API 19) 以上的各 Android 版本內的 SDK Platform 都安裝起來 )
    • Extras > Android Support Repository
    • Extras > Google USB Driver

2017/01/07

Xamarin FAQ 1-005 : 如何在原生專案使用 Prism 事件聚合器 (Event Aggregator)來非同步呼叫核心 PCL 專案內方法

問題

有時候,當程式碼已經轉移到原生專案組件內執行了,此時,您要用非同步的使用核心 PCL 專案內的某個方法,例如,呼叫核心 PCL 專案內的某個方法,使用導航物件進行頁面導航的工作,這個要如何做到呢?

解答

要解決這樣的需求,請依照底下的步驟進行操作。
  • 請先取得這個應用程式的 Prism 容器 ( Container ),請使用底下方法取得 Prism 容器
            // 取得 Prism 相依性服務使用到的容器
            IUnityContainer fooContainer = (XFoAuth2.App.Current as PrismApplication).Container;
  • 接著,解析出這事件聚合器 (Event Aggregator)介面的實作物件,一旦您取得了這個實作物件,您就可以使用 Prism 的事件訂閱或者發佈事件功能。
            // 取得 IAccountStore 介面實際實作的類別物件
            var fooIEventAggregator = fooContainer.Resolve<IEventAggregator>();
  • 在底下的範例中,會根據 e.IsAuthenticated 的值內容,使用 Prism 的事件聚合器 (Event Aggregator),送出不同的事件訊息給核心 PCL 專案的訂閱者。
            if (e.IsAuthenticated)
            {
                fooIEventAggregator.GetEvent<AuthEvent>().Publish(AuthEventEnum.身分驗證成功);
            }
            else
            {
                fooIEventAggregator.GetEvent<AuthEvent>().Publish(AuthEventEnum.身分驗證失敗);
            }
  • 在核心 PCL 專案內,使用了 Prism 的事件聚合器 (Event Aggregator)訂閱了這個事件,一旦非同步收到這個訊息,就會回到上一頁頁面。
        public oAuthPageViewModel(INavigationService navigationService, IEventAggregator eventAggregator)
        {

            _eventAggregator = eventAggregator;
            _navigationService = navigationService;

            // 訂閱使用者認證結果通知事件,認證成功之後,會收到 Success
            fooSubscriptionToken = _eventAggregator.GetEvent<AuthEvent>().Subscribe(async x =>
              {
                  await _navigationService.GoBackAsync();
              });
        }

2017/01/06

對於 Xamarin 技能實驗室的建議

若您對於  Xamarin 技能實驗室有:


  • 任何的建議與指教
  • 想要了解的 Xamarin 相關技術或文章
  • 提供想要寫入 Xamarin FAQ 內的文章
  • 關於 Xamarin 教育訓練課程的問題
歡迎您在這篇文章留言處,寫下您的意見,感謝您的支持。

Xamarin FAQ 1-004 : 如何從PCL來同步呼叫原生專案內實作方法

問題

在進行 Xamarin.Forms 專案開發的時候,必定會使用這這樣的需求,那就是有些功能在核心 PCL 專案內是無法使用的,需要在原生專案內實作出這些功能,並且,在每個不同平台的原生專案內,又需要使用不同原生專案支援的類別庫,來完成這些功能需求。這個時候,您可以使用 Prism 的相依性服務機制,在每個不同平台來實作出這些功能,透過相依性服務注入機制,在檢視模型 (ViewModels)內,使用建構式注入的方式,取得這些實作物件,如此,就可以在核心 PCL 專案內呼叫這些原生專案獨有的功能了。

解答

要解決這樣的需求,請依照底下的步驟進行操作。
  • 首先,在核心 PCL 專案內,產生一個介面 (Interface),在這個介面內宣告出共用的方法。在底下,定義了一個介面,裡面有個方法 GetPlatformName,我們期望能夠在核心 PCL 專案內呼叫這個方法,就可以知道當時是在哪個平台作業系統上執行。
    public interface INatvieAssyFile
    {
        /// <summary>
        /// 取得作業系統版本名稱
        /// </summary>
        /// <returns></returns>
        string GetPlatformName();
    }
  • 接著,您需要在每個原生平台(Android, iOS, UWP)專案中,產生一個類別,並且實作出這個介面功能。在底下的範例中,我們產生了一個 NatvieAssyFile類別,並且實作了介面 INatvieAssyFile的功能;因為這個類別是定義在 iOS 專案內,所以方法 GetPlatformName() 就僅會回傳 iOS 這個字串。
    如此,當這個 Xamarin.Forms 的專案在 iOS 系統下運行的時候,並且程式在核心 PCL 專案內執行呼叫 GetPlatformName()方法,就會得到了 iOS 這個字串;當然,您需要其他原生專案內也實作出同樣的類別,並且回傳當時作業系統的文字。
  • 另外,您需要在 namespace 前面,宣告這個類別可以具備相依性服務注入的能力,在這個範例中,使用了這樣的語法 [assembly: Xamarin.Forms.Dependency(typeof(NatvieAssyFile))]。加入了這行,您就可以在核心 PCL 專案內,使用建構式注入的方式,取得這個實作物件了。
[assembly: Xamarin.Forms.Dependency(typeof(NatvieAssyFile))]
namespace XFFiles.iOS.Services
{
    class NatvieAssyFile : INatvieAssyFile
    {
        public string GetPlatformName()
        {
            return "iOS";
        }
    }
}
  • 最後,我們來看如何在核心 PCL 專案內,取得這個注入物件;要做到取得注入物件,請參考底下步驟:
    • 宣告一個 INatvieAssyFile 介面變數,用來持有注入後的實作物件。
    • 在檢視模型的建構式中,加入這個引數 INatvieAssyFile natvieAssyFile
    • 在檢視模型的建構式裡面,將剛剛注入進來的物件,設定讓欄位變數可以持有這個傳入的物件。
        private readonly INavigationService _navigationService;
        private readonly INatvieAssyFile _natvieAssyFile;
        #endregion

        #region Constructor 建構式
        public MainPageViewModel(INavigationService navigationService, INatvieAssyFile natvieAssyFile)
        {

            _navigationService = navigationService;
            _natvieAssyFile = natvieAssyFile;
            ...
        }

2017/01/05

Xamarin FAQ 1-003 : 如何使用容器註冊介面與實作物件

問題

在 Xamarin.Forms 內,若您使用了 Prism Template Pack 來建立一個檢視 (View),此時,Visua Studio 除了幫您建立一個 XAML 檢視頁面之外,還會幫您一併在 ViewModels 資料夾下也建立一個檢視模型,有些時候,想要在核心 PCL 專案內或只者原生專案內,註冊一個介面與其實作類別,那麼該怎麼做呢?

解答

這個時候,您需要先取得 Prism 的容器 Container,接著使用該容器提供的 RegisterType 方法,不過,需要將介面與實作類別型別使用泛型方式傳入進去,這樣就完成了註冊的工作。
經過這樣的註冊程序,若要透過相依性服務取得實作的物件,則是具備了短暫(transient)生命週期(lifetime);也就是,每進行注入解析動作,就會產生一個不同的物件出來。
myContainer.RegisterType<IMyClass, MyClass>();

2017/01/04

Xamarin FAQ 1-002 : 如何不透過建構式注入的方式,取得特定介面的實作物件

問題

當我們在使用 Prism 相依性服務注入功能的時候,通常會使用建構式注入方式,取的介面的實作物件,但有些時候,我們希望直接透過容器來取得這個實作物件,那麼,該如何做到呢?

解答

當您已經取得了 Prism 的容器物件,例如,該容器儲存在 myContainer 這個物件內,您可以使用 myContainer.Resolve<IMyInterface>(); 這樣的方式,取得介面 IMyInterface 的實作物件到 fooObject內。
+

IMyInterface fooObject = myContainer.Resolve<IMyInterface>();

2017/01/03

Xamarin FAQ 1-001 : 如何取得 Prism 的容器 Container 物件

問題

當您需要使用 Prism 功能,進行相依性服務的註冊與解析功能的時候,需要取得 Prism 的容器 Container 物件,那麼,在不同的組件 (Assembly)中(原生專案或者核心 PCL 專案),要如何取得這個容器物件呢?

解答

想要取得 Prism 的容器物件,可以使用底下程式碼
IUnityContainer myContainer = (App.Current as PrismApplication).Container;
這樣就可以取得 IUnityContainer 的容器物件,透過這個物件,您可以輕鬆的進行注入您所想要的相依性物件到您現在的類別方法內。

2016/12/31

Xamarin.Forms 無法進行 Android 實體手機除錯

當您在進行 Xamarin.Forms 專案開發的時候,您的 Android 專案可以正常在模擬器上執行與除錯,可是,當要在實體 Android 手機上進行除錯的時候,卻得到底下錯誤訊息:
Couldn't connect to logcat, GetProcessId returned: 0 
這個時候,您可以使用滑鼠雙擊 Android 專案下的 Properties 項目,接著,在 Properties 視窗中,點選 Android Option > Packaging ,請取消 Use Fast Depolyment (debug mode only) 這個選項。
接著,請清除整個方案,重新建置 Android 專案,接著進行除錯,相信您的應用程式,已經可以在實體手機上進行除錯了。

2016/12/23

如何使用 Zoom 的功能

若您是第一次使用 Zoom,並且已經進入到全螢幕模式,這個時候,請將游標移到中間最上方的區域,此時,會出現 Zoom 的彈出功能表,您可以點選 [退出全螢幕] 功能,就會進入到 Zoom 的視窗模式。



當進入到視窗模式,左下方有一個麥克風圖示,儘可能切換到下圖的麥克風模式,也就是有條紅色斜線的模式,在這個模式下,你的麥克風是在靜音模式;這樣做的目的是,儘可能不會因為你的麥克風有啟動,造成有不同的聲音在會議中,使得會議品質降低。

不過,當您需要講話的時候,可以點選左下角的麥克風圖示,讓麥克風可以啟用,這樣,您就可以透過麥克風,與會議中的所有人溝通。



若需要使用聊天室功能,請點選最下方的 (1) [聊天] 按鈕,此時,右方會出現一塊 (2) 區域,您可以在右下方的區域打入您要讓其他人看到的文字,大家打的文字,就會在 (3) 區域顯示出來。

若在會議中或者任何時候,可以點選 (4) [舉手] 功能,此時,會議主持人就會看到你的舉手動作,這樣,會議主持人就會可以與你產生互動,或者,讓你透過啟用麥克風,讓您可以在會議中講話。

2016/12/11

設計可以動態繪製2D圖形的 Xamarin.Forms App

SkiaSharp是可以在 Xamarin.Forms 的核心PCL專案內,產生 2D 圖形的一個控制項,有了這個擴充控制項,您可以使用 Xamarin.Forms 開發出一個資料蒐集應用程式,並且可以將蒐集到的數據,以圖形的方式顯示在手機螢幕上。
底下為這個範例程式的執行結果。
在這個範例中將會展示如何使用 SkiaSharp 這個套件,在核心PCL專案內,進行2D圖形繪製,要使用這個功能,您需要在方案內的所有專案,安裝這個 SkiaSharp.Views.Forms NuGet 套件。
這個範例專案的原始碼位於
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
             xmlns:views="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             prism:ViewModelLocator.AutowireViewModel="True"
             x:Class="XFSkiaSharp.Views.MainPage"
             Title="MainPage">
    <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
        <views:SKCanvasView  
            HorizontalOptions="Fill" VerticalOptions="Start"
            HeightRequest="400"
            x:Name="Canvas" PaintSurface="OnPaintSample"
            Margin="0,0,0,0" />
        <Label Text="{Binding Title}" />
        <StackLayout
            Orientation="Horizontal">
            <Button x:Name="myBtn收集資料" Text="收集資料" />
            <Button x:Name="myBtn畫點" Text="畫點" />
            <Button x:Name="myBtn畫線" Text="畫線" />
            <Button x:Name="myBtn清除" Text="清除" />
        </StackLayout>
    </StackLayout>
</ContentPage>
using SkiaSharp;
using Xamarin.Forms;
using SkiaSharp.Views.Forms;
using System;

namespace XFSkiaSharp.Views
{
    public enum 重繪狀態
    {
        第一次初始化,
        清除,
        繪製點,
        繪製線,
        收集資料
    }
    public partial class MainPage : ContentPage
    {
        public 重繪狀態 _重繪狀態 { get; set; } = 重繪狀態.第一次初始化;
        public MainPage()
        {
            InitializeComponent();

            myBtn清除.Clicked += (s, e) =>
              {
                  _重繪狀態 = 重繪狀態.清除;
                  Canvas.InvalidateSurface();
              };

            myBtn畫線.Clicked += (s, e) =>
              {
                  _重繪狀態 = 重繪狀態.繪製線;
                  Canvas.InvalidateSurface();
              };

            myBtn畫點.Clicked += (s, e) =>
            {
                _重繪狀態 = 重繪狀態.繪製點;
                Canvas.InvalidateSurface();
            };
            myBtn收集資料.Clicked += (s, e) =>
            {
                _重繪狀態 = 重繪狀態.收集資料;
                Canvas.InvalidateSurface();
            };
        }

        int surfaceWidth;
        int surfaceHeight;
        SKCanvas myCanvas;
        private void OnPaintSample(object sender, SkiaSharp.Views.Forms.SKPaintSurfaceEventArgs e)
        {
            surfaceWidth = e.Info.Width;
            surfaceHeight = e.Info.Height;
            myCanvas = e.Surface.Canvas;
            float side = Math.Min(surfaceHeight, surfaceWidth) * 0.5f;

            switch (_重繪狀態)
            {
                case 重繪狀態.第一次初始化:
                    myCanvas.Clear(Color.Gray.ToSKColor());  //paint it black
                    break;
                case 重繪狀態.清除:
                    myCanvas.Clear(Color.Black.ToSKColor());  //paint it black
                    myCanvas.Clear(Color.Red.ToSKColor());  //paint it black
                    myCanvas.Clear(Color.Blue.ToSKColor());  //paint it black
                    myCanvas.Clear(Color.Gray.ToSKColor());  //paint it black
                    break;
                case 重繪狀態.繪製點:
                    var foo = 1;
                    using (SKPaint paint = new SKPaint())
                    {
                        paint.Color = Color.FromHex("00FF00").ToSKColor();
                        paint.StrokeWidth = 5;
                        Random rm = new Random();
                        for (int i = 0; i < 3000; i++)
                        {
                            //myCanvas.DrawPoint(rm.Next(300), rm.Next(300), SKColor.Parse("#00eFF00"));
                            myCanvas.DrawPoint(rm.Next(surfaceWidth), rm.Next(surfaceHeight), paint);
                        }
                    }
                    break;
                case 重繪狀態.收集資料:
                    using (SKPaint paint = new SKPaint())
                    {
                        paint.Color = Color.FromHex("FF0000").ToSKColor();
                        paint.StrokeWidth = 3;
                        Random rm = new Random();
                        for (int i = 0; i < surfaceWidth; i++)
                        {
                            var fooPM = rm.Next(10);
                            if (fooPM >= 5)
                            {
                                fooPM = -1;
                            }
                            else
                            {
                                fooPM = 1;
                            }

                            var fooY = surfaceHeight - (32 + i / 3 + (rm.Next(10) * fooPM));
                            var fooX = i;
                            myCanvas.DrawPoint(fooX, fooY, paint);
                        }
                    }
                    break;
                case 重繪狀態.繪製線:
                    using (SKPaint paint = new SKPaint())
                    {
                        paint.Color = Color.Pink.ToSKColor();
                        paint.StrokeWidth = 7;
                        Random rm = new Random();
                        var fooPreX = 0;
                        var fooPreY = surfaceHeight - 100;
                        for (int i = 0; i < surfaceWidth / 10; i++)
                        {
                            var fooPM = rm.Next(13);
                            if (fooPM >= 5)
                            {
                                fooPM = -1;
                            }
                            else
                            {
                                fooPM = 1;
                            }

                            var fooY = surfaceHeight - (100 + i + (rm.Next(50) * fooPM));
                            var fooX = i * 10;
                            myCanvas.DrawLine(fooPreX, fooPreY, fooX, fooY, paint);
                            fooPreX = fooX;
                            fooPreY = fooY;
                        }
                    }
                    break;
                default:
                    break;
            }
            return;
            using (SKPaint paint = new SKPaint())
            {
                myCanvas.Clear(Color.Gray.ToSKColor());  //paint it black
                SKRect r1 = new SKRect(10f, 20f, side, side);
                paint.Color = Color.Blue.ToSKColor();
                myCanvas.DrawRect(r1, paint);

                paint.Color = Color.Red.ToSKColor();
                myCanvas.DrawOval(r1, paint);

                paint.Color = Color.FromHex("00FF00").ToSKColor();
                paint.StrokeWidth = 5;

                Random rm = new Random();
                for (int i = 0; i < 3000; i++)
                {
                    myCanvas.DrawPoint(rm.Next(300), rm.Next(300), paint);
                    //myCanvas.DrawPoint(rm.Next(300), rm.Next(300), SKColor.Parse("#00FF00"));
                }

                paint.Color = Color.Green.ToSKColor();
                paint.TextSize = 64.0f;
                paint.IsAntialias = true;
                paint.Color = new SKColor(0x9C, 0xAF, 0xB7);
                paint.IsStroke = true;
                paint.StrokeWidth = 3;
                paint.TextAlign = SKTextAlign.Center;

                myCanvas.DrawText("這是Xamarin.Forms", 200f, 200f, paint);
                myCanvas.DrawText("這是Xamarin.Forms", surfaceWidth / 2f, surfaceHeight / 2f, paint);
            }
        }
    }
}