浏覽和等待
ImageBrowser程式示範了Image的另一個功能,它允許您浏覽本書中某些示例所使用的庫存照片。 正如您在下面的XAML檔案中看到的那樣,Image元素與Label和兩個Button視圖共享螢幕。 請注意,在Image上設定了PropertyChanged處理程式。 您在第11章“可綁定基礎結構”中了解到,PropertyChanged處理程式由BindableObject實作,并在綁定屬性更改值時觸發。
<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 x:Name="image"
VerticalOptions="CenterAndExpand"
PropertyChanged="OnImagePropertyChanged" />
<Label x:Name="filenameLabel"
HorizontalOptions="Center" />
<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>
此頁面上還有一個ActivityIndicator。 當程式等待長操作完成(例如下載下傳位圖)但通常無法提供有關操作進度的任何資訊時,通常會使用此元素。 如果您的程式知道操作的完成部分,則可以使用ProgressBar。 (ProgressBar将在下一章示範。)
ActivityIndicator有一個名為IsRunning的布爾屬性。通常,該财産是
false,ActivityIndicator不可見。将該屬性設定為true可使ActivityIn?dicator可見。所有這三個平台都實作了一個動畫視覺,表明該程式正在運作,但在每個平台上看起來都有點不同。在iOS上它是一個旋轉輪,在Android上它是一個旋轉的部分圓圈。在Windows裝置上,一系列點在螢幕上移動。
為了提供對庫存圖像的浏覽通路,ImageBrowser需要下載下傳包含所有檔案名清單的JSON檔案。多年來,各種版本的.NET引入了幾個能夠通過Web下載下傳對象的類。但是,并非所有這些都可用于可移植類庫中的.NET版本,該類庫具有與Xamarin.Forms相容的配置檔案。可用的類是WebRequest及其後代類HttpWebRequest。
WebRequest.Create方法基于URI傳回WebRequest方法。 (傳回值實際上是一個HttpWebRequest對象。)BeginGetResponse方法需要一個回調函數,當引用URI的Stream可用于通路時,該函數被調用。通過調用EndGetResponse和GetResponseStream可以通路Stream。
一旦程式在以下代碼中通路Stream對象,它就會使用DataCon?tractJsonSerializer類以及在ImageBrowserPage類頂部附近定義的嵌入式ImageList類,将JSON檔案轉換為ImageList對象:
public partial class ImageBrowserPage : ContentPage
{
[DataContract]
class ImageList
{
[DataMember(Name = "photos")]
public List<string> Photos = 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);
}
void WebRequestCallback(IAsyncResult result)
{
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()
{
// Prepare for new image.
image.Source = null;
string url = imageList.Photos[imageListIndex];
// Set the filename.
filenameLabel.Text = url.Substring(url.LastIndexOf('/') + 1);
// Create the UriImageSource.
UriImageSource imageSource = new UriImageSource
{
Uri = new Uri(url + "?Width=1080"),
CacheValidity = TimeSpan.FromDays(30)
};
// Set the Image source.
image.Source = imageSource;
// Enable or disable buttons.
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;
}
}
}
WebRequestCallback方法的整個主體都包含在lambda函數中,該函數是Device.BeginInvokeOnMainThread方法的參數。 WebRequest下載下傳由輔助執行線程中的URI引用的檔案。這可以確定操作不會阻止正在處理使用者界面的程式的主線程。回調方法也在此輔助線程中執行。但是,可以通路Xamarin.Forms應用程式中的使用者界面對象
僅來自主線程。
Device.BeginInvokeOnMainThread方法的目的是解決此問題。此方法的參數排隊等待在程式的主線程中運作,并可以安全地通路使用者界面對象。
當您單擊這兩個按鈕時,對FetchPhoto的調用使用UriImageSource來下載下傳新位圖。這可能需要一秒鐘左右。 Image類定義一個名為IsLoading的Boolean屬性,當Image處于加載(或下載下傳)位圖的過程中時,該屬性為true。 IsLoading由可綁定屬性IsLoadingProperty支援。這也意味着每當IsLoading更改值時,都會觸發PropertyChanged事件。該程式使用PropertyChanged事件處理程式 - 類的最底部的OnImagePropertyChanged方法 - 将ActivityIndicator的IsRunning prop.erty設定為與Image的IsLoading屬性相同的值。
您将在第16章“資料綁定”中看到,您的應用程式如何連結IsLoading和IsRunning等屬性,以便它們在沒有任何顯式事件處理程式的情況下保持相同的值。
這是ImageBrowser的實際應用:

某些圖像設定了EXIF方向标志,如果特定平台忽略該标志,則圖像會側向顯示。
如果以橫向模式運作此程式,您将發現按鈕消失。 這個程式的更好的布局選項是Grid,第17章對此進行了示範。