天天看點

第十三章:位圖(五)

浏覽和等待

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将在下一章示範。)

ActivityIndi​​cator有一個名為IsRunning的布爾屬性。通常,該财産是

false,ActivityIndi​​cator不可見。将該屬性設定為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方法 - 将ActivityIndi​​cator的IsRunning prop.erty設定為與Image的IsLoading屬性相同的值。

您将在第16章“資料綁定”中看到,您的應用程式如何連結IsLoading和IsRunning等屬性,以便它們在沒有任何顯式事件處理程式的情況下保持相同的值。

這是ImageBrowser的實際應用:

第十三章:位圖(五)

某些圖像設定了EXIF方向标志,如果特定平台忽略該标志,則圖像會側向顯示。

如果以橫向模式運作此程式,您将發現按鈕消失。 這個程式的更好的布局選項是Grid,第17章對此進行了示範。