2017/4/10

Xamarin.Forms 教學系列文(十三.壹)從網址或內嵌資源載入圖片



學習目標
  • ImageSource.FromUri()  - 從網址讀圖
  • Aspect - 圖片填滿方式
  • ImageSource.FromResource() - 從 PCL 專案讀圖

由於各家平台顯示 Bitmaps 方式不太一樣。

於是 Xamrin 提供了 Image(我是類別) 來統合處理 Bitmaps 圖片的顯示。

Image 最重要的屬性為 Source,用來指定圖片的來源,其方法有四:
  • ImageSource.FromUri() - 從網址載入
  • ImageSource.FromResource() - 從 PCL 內嵌資源載入
  • ImageSource.FromFile() - 從各平台專案內載入
  • ImageSource.FromStream() - 從 .NET 提供的 Stream 載入

一般來說,FromUri() FromResource() 用來讀取跟平台 無相依性 的圖片。

FromFile() 則用來針對不同平台取得不同的圖片,例如 MenuItem 或 ToolbarItem 的 Icon。


對應這四種方法,Image 還有三個子類別,UriImageSource、FileImageSource & StreamImageSource。

雖然在 .cs 內用 ImageSorce.FromXXX() 方法直接指定圖片來源比較方便,不過偶爾還是會在 XAML 內使用這三個子類別。


這章節會先從 FromUri()FromResource() 開始~


FromUri()

底下是一支從 Uri 讀取圖片的 cs 寫法:
public class WebBitmapCodePage : ContentPage
{
    public WebBitmapCodePage()
    {
        string uri = "https://developer.xamarin.com/demo/IMG_1415.JPG";

        Content = new Image
        {
            Source = ImageSource.FromUri(new Uri(uri))
        };
    }
}

或是直接指派 Source,程式會幫你做隱含轉換:
public class WebBitmapCodePage : ContentPage
{
    public WebBitmapCodePage()
    {
        Content = new Image
        {
            Source = "https://developer.xamarin.com/demo/IMG_1415.JPG"
        };
    }
}

或是你想寫詳細一點:
public class WebBitmapCodePage : ContentPage
{
    public WebBitmapCodePage()
    {
        Content = new Image
        {
            Source = new UriImageSource
            {
                Uri = new Uri("https://developer.xamarin.com/demo/IMG_1415.JPG")
            }
        };
    }
}


執行結果:

既然用網址載圖,UriImageSource 提供了兩個快取屬性可設定:
  • CachingEnabled - 是否使用快取,預設為 True
  • CachingValidity - 快取保留時間,類別為 TimeSpan,預設是一天


若今天將來源圖檔寬度縮小剩 25:
Content = new Image { Source = "https://developer.xamarin.com/demo/IMG_1415.JPG?width=25" };

會變成這樣 (好像哪裡怪怪的? 下面會解釋...):


XAML 寫法:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="WebBitmapXaml.WebBitmapXamlPage">

    <Image Source="https://developer.xamarin.com/demo/IMG_3256.JPG" />

</ContentPage>

詳細一點也可以寫成:
<Image>
    <Image.Source>
        <UriImageSource Uri="https://developer.xamarin.com/demo/IMG_3256.JPG" />
    </Image.Source>
</Image>

執行結果:


Aspect - 

Image 有個屬性 - Aspect,用來設定圖片顯示於容器的方式

成員有三:
  • AspectFit - 預設依比例縮放,適應容器
  • Fill - 不管比例縮放,填滿容器
  • AspectFill - 依比例縮放,填滿容器 (會截圖) 

上一小節的圖片即使來源圖片變小了,但預設還是 AspectFit,才會將輸出結果縮放到容器適應的大小。
Fill -
<Image Source="https://developer.xamarin.com/demo/IMG_3256.JPG" Aspect="Fill" />


AspectFill -
<Image Source="https://developer.xamarin.com/demo/IMG_3256.JPG" Aspect="AspectFill" />



FromResource()

藉由 Uri 讀取圖片是一個相當方便的方法,卻也有相當多限制,例如需要網路,圖片是否存在,需要時間去下載…

為了更快速的讀取圖片,其實可以將圖片嵌入至專案內,並使用 ImageResource.FromResource() 

我們可以先在 PCL 內新增一個 Images 的資料夾,並把你要使用的圖片放進此資料夾,接著
在 VS 的方案總管,對圖檔按右鍵 → 屬性 → 建置動作改成內嵌資源
(Embedded resources)



專案建置後就會得到一組由 專案名.資料夾.圖檔名 組成的 Resource ID

原文書的範例如下:
ResourceBitmapCode.Images.ModernUserInterface256.jpg

FromResource 使用範例:
public class ResourceBitmapCodePage : ContentPage
{
    public ResourceBitmapCodePage()
    {
        Content = new Image
        {
            Source = ImageSource.FromResource("ResourceBitmapCode.Images.ModernUserInterface256.jpg"),
            VerticalOptions = LayoutOptions.Center,
            HorizontalOptions = LayoutOptions.Center
        };
    }
}

執行結果:

可以發現這次圖片沒有延展,原因為:
  • 當圖片比容器小,且VerticalOptions 和 HorizontalOptions 不是設定 Fill ( 或 Image 放在 StackLayout 內 )

要怎麼在 XAML 內使用 FromResource 呢?

很抱歉,目前沒有提供方法… 現今(20170410) 最簡單的方法是自己寫一個擴充標籤…

客制化擴充標籤的教學在 第十章

程式碼如下…
namespace StackedBitmap
{
    [ContentProperty("Source")]  //不用在XAML內明確的寫 "Source="
    public class ImageResourceExtension : IMarkupExtension
    {
        public string Source { get; set; }

        public object ProvideValue(IServiceProvider serviceProvider)
        {
            if (Source == null)
                return null;

            return ImageSource.FromResource(Source);
        }
    }
}

XAML 用法:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:local="clr-namespace:StackedBitmap " 
             x:Class="StackedBitmap.StackedBitmapPage">
  <StackLayout>
    <Label Text="320 x 240 Pixel Bitmap" 
           FontSize="Medium" 
           VerticalOptions="CenterAndExpand" 
           HorizontalOptions="Center" />
    
    <!--使用 ImageResource 擴充標籤-->
    <Image Source="{local:ImageResource StackedBitmap.Images.Sculpture_320x240.jpg}" 
           BackgroundColor="Aqua" 
           SizeChanged="OnImageSizeChanged" />
    
    <Label x:Name="label" 
           FontSize="Medium" 
           VerticalOptions="CenterAndExpand" 
           HorizontalOptions="Center" />
  
  </StackLayout>
</ContentPage>

SizeChanged 事件,將 Image 的寬高顯示於前端 Label:
public partial class StackedBitmapPage : ContentPage
{
    public StackedBitmapPage()
    {
        InitializeComponent();
    }

    void OnImageSizeChanged(object sender, EventArgs args)
    {
        Image image = (Image)sender;
        label.Text = String.Format("Render size = {0:F0} x {1:F0}", image.Width, image.Height);
    }
}

由於將 Image 背景色設定為 Aqua,很清楚地可以看到 Image 和輸出的 bitmap 關係:

由於 Image 放在 StackLayout 內,其高度會先受到限制,而 HorizontalOptions 預設是 Fill,Image 就會往左右延展。

但圖片本身預設 AspectFit,就會看到 Image 和 Bitmap 比例不同的狀況如上圖。

如果你想將 Image 的比例和 Bitmap 輸出的結果比例一樣,可以設定 HorizontalOptions = "Center"
<Image Source="{local:ImageResource StackedBitmap.Images.Sculpture_320x240.jpg}" 
       HorizontalOptions="Center" 
       BackgroundColor="Aqua"
       SizeChanged="OnImageSizeChanged" />



設定圖片寬與高

可以在 Image 發現 Width 和 Height 這兩個屬性,但在 Xamarin 內這兩個屬性是 唯獨 的!!

意思就是設定了也不會有作用...

第五章 - 單位 有提到,若要手動更改 Visual Element 寬與高要設定這兩個屬性:
  • HeightRequest
  • WidthRequest 





沒有留言:

張貼留言