學習目標
- Device.BeginInvokeOnMainThread 非同步
- ActivityIndicator 轉圈圈等待圖
會將這支小程式獨立出來講解,主要原因是從 Uri 讀取圖片時會有短暫的延遲,
若有大量圖片要讀取話,一定會產生不好的使用者經驗,所以必須搭配非同步的方法來載圖。
直接上程式碼:
前端很單純,最上方一個 Image,中間一個 Label 顯示檔案名稱,一個 ActivityIndicator 轉圈圈,兩個 Button 切換前後張圖。
要注意的是 ActivityIndicator 這 Visual Element,屬性 IsRunning 預設值是 False,所以預設是隱藏的 。
若將 IsRunning 設為 True 就會顯示 轉圈圈的的等待圖,每個平台都有預設這個物件和方法,但若你知道程式 執行的時間,可以用下一章節提到的 ProgressBar。
另外 Image 的 OnImagePropertyChanged 是用來配合 ActivityIndicator,讓 ActivityIndicator 知道圖片什麼時候載入。
*OnPlatform 寫法已更改,請參考此篇文章
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="ImageBrowser.ImageBrowserPage"> <ContentPage.Padding> <OnPlatform x:TypeArguments="Thickness" iOS="0, 20, 0, 0"/> </ContentPage.Padding> <StackLayout> <!--Image--> <Image x:Name="image" VerticalOptions="CenterAndExpand" PropertyChanged="OnImagePropertyChanged" /> <Label x:Name="filenameLabel" HorizontalOptions="Center" /> <!--ActivityIndicator--> <ActivityIndicator x:Name="activityIndicator"/> <StackLayout Orientation="Horizontal"> <Button x:Name="prevButton" Text="Previous" IsEnabled="false" HorizontalOptions="CenterAndExpand" Clicked="OnPreviousButtonClicked" /> <Button x:Name="nextButton" Text="Next" IsEnabled="false" HorizontalOptions="CenterAndExpand" Clicked="OnNextButtonClicked" /> </StackLayout> </StackLayout> </ContentPage>
.cs 要注意的是,Xamarin.Forms 內只有 WebRequest 這個物件可以下載 Http 的東西。
而 WebRequestCallback 內有個 Device.BeginInvokeOnMainThread(() => ,用來開啟另一條 Tread 非同步執行動作,程式才不會因為讀圖的延遲造成 MainThread 鎖死。
WebRequest 讀取結束後利用 .EndGetResponse(result).GetResponseStream() 回傳 Stream 物件,再將回傳的 Stream 物件解序列成 ImageList 類別的物件。
最後 FetchPhoto() 方法內將圖片載入。
Code - behind
using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Net; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using Xamarin.Forms; namespace ImageBrowser { public partial class ImageBrowserPage : ContentPage { [DataContract] class ImageList { [DataMember(Name = "photos")] public ListPhotos = null; } WebRequest request; ImageList imageList; int imageListIndex = 0; public ImageBrowserPage() { InitializeComponent(); // Get list of stock photos. Uri uri = new Uri("https://developer.xamarin.com/demo/stock.json"); request = WebRequest.Create(uri); request.BeginGetResponse(WebRequestCallback, null); } private void InitializeComponent() { throw new NotImplementedException(); } void WebRequestCallback(IAsyncResult result) { //非同步方法,開啟另一條 Thread Device.BeginInvokeOnMainThread(() => { try { Stream stream = request.EndGetResponse(result).GetResponseStream(); // Deserialize the JSON into imageList; var jsonSerializer = new DataContractJsonSerializer(typeof(ImageList)); imageList = (ImageList)jsonSerializer.ReadObject(stream); if (imageList.Photos.Count > 0) //載圖 FetchPhoto(); } catch (Exception exc) { filenameLabel.Text = exc.Message; } }); } void OnPreviousButtonClicked(object sender, EventArgs args) { imageListIndex--; FetchPhoto(); } void OnNextButtonClicked(object sender, EventArgs args) { imageListIndex++; FetchPhoto(); } void FetchPhoto() { // 準備新的 Image image.Source = null; string url = imageList.Photos[imageListIndex]; // 檔案名稱 filenameLabel.Text = url.Substring(url.LastIndexOf('/') + 1); // Create the UriImageSource. UriImageSource imageSource = new UriImageSource { Uri = new Uri(url + "?Width=1080"), CacheValidity = TimeSpan.FromDays(30) }; // 設定 Source image.Source = imageSource; // 設定 Button 是否能作用 prevButton.IsEnabled = imageListIndex > 0; nextButton.IsEnabled = imageListIndex < imageList.Photos.Count - 1; } void OnImagePropertyChanged(object sender, PropertyChangedEventArgs args) { if (args.PropertyName == "IsLoading") { activityIndicator.IsRunning = ((Image)sender).IsLoading; } } } }
前一章節有提到從 Uri 讀取圖片時,會有快取暫存在手機內,所以當按鈕切換到已經載入過的圖,會發現速度快很多。
沒有留言:
張貼留言
注意:只有此網誌的成員可以留言。