Windows Phone 8.1从远程URL加载图片的占位符

3

我正在尝试一些Windows Phone开发,因为我之前是从Android背景过来的。

我正在使用“theCatAPI”来加载一张随机猫图片并展示它,然后当图片被点击或屏幕底部的按钮被点击时,图片会刷新到新的一张。

目前我已经有了以下内容:

XAML:

<Page
    x:Class="CatFactsPics.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:CatFactsPics"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid x:Name="LayoutRoot">

        <Grid.ChildrenTransitions>
            <TransitionCollection>
                <EntranceThemeTransition/>
            </TransitionCollection>
        </Grid.ChildrenTransitions>

        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!-- TitlePanel -->
        <StackPanel Grid.Row="0" Margin="24,17,0,28">
            <TextBlock Text="My Application" Style="{ThemeResource TitleTextBlockStyle}" Typography.Capitals="SmallCaps"/>
            <TextBlock Text="page title" Margin="0,12,0,0" Style="{ThemeResource HeaderTextBlockStyle}"/>
        </StackPanel>

        <!--TODO: Content should be placed within the following grid-->
        <Grid Grid.Row="1" x:Name="ContentRoot">
            <Image HorizontalAlignment="Center" Stretch="UniformToFill" VerticalAlignment="Center" x:Name="KittyPic" Tapped="KittyPic_Tapped"/>
            <Button HorizontalAlignment="Center" VerticalAlignment="Bottom" x:Name="newPic" Click="newPic_Click" >New Kitty</Button>
        </Grid>
    </Grid>
</Page>

在page.cs中:
...
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            this.navigationHelper.OnNavigatedTo(e);

            Uri myUri = new Uri("http://thecatapi.com/api/images/get?format=src&type=jpg", UriKind.Absolute);
            KittyPic.Source = new BitmapImage(myUri);
        }
...
        private void newPic_Click(object sender, RoutedEventArgs e)
        {
            Uri myUri = new Uri("http://thecatapi.com/api/images/get?format=src&type=jpg", UriKind.Absolute);
            BitmapImage bmi = new BitmapImage();
            bmi.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
            bmi.UriSource = myUri;
            KittyPic.Source = bmi;
        }

我有几个问题:

1)这是正确的做法吗?在Android中,我会尝试异步处理来避免阻塞UI线程。话虽如此,目前似乎没有任何问题。我不熟悉Windows的处理方式,也没有找到任何解释或建议。

2)显示新图片存在延迟,导致图像视图在新图像重新出现之前变成黑色(约几秒钟)。有没有办法设置让旧图片保持不变,直到新图片准备好显示出来,或者同时显示一个占位符“加载”图像,直到新图片可以替换它。

如果有其他建议或提示,将不胜感激,谢谢。

2个回答

5

1) 您当前的代码不会阻塞UI线程,因为您是在UI线程上设置URI,但实际载入图像是在另一个线程自动完成的。(对于手动下载图像、字符串等,您可能会使用async/await来避免锁定UI线程)。

2) 图像变黑是因为您在新图像加载之前更改了ImageSource。正如您提到的,有几种处理方式。其中大多数都需要在Image控件上使用ImageOpenedImageFailed事件,在图像加载完成时(或出现错误,例如没有互联网连接)触发这些事件。以下是显示加载进度条的示例,它只是隐藏/显示加载进度:

page.xaml文件中:

<Grid Grid.Row="1" x:Name="ContentRoot">
    <Image x:Name="KittyPic" Tapped="KittyPic_Tapped" ImageOpened="KittyPic_ImageOpened" ImageFailed="KittyPic_ImageFailed" />
    <StackPanel x:Name="LoadingPanel" VerticalAlignment="Center">
        <ProgressBar IsIndeterminate="True" IsEnabled="True" />
        <TextBlock Text="Loading image..." HorizontalAlignment="Center" />
    </StackPanel>
</Grid>

在page.xaml.cs文件中

private void KittyPic_Tapped(object sender, TappedRoutedEventArgs e)
{
    LoadingPanel.Visibility = Visibility.Visible;
    Uri myUri = new Uri("http://thecatapi.com/api/images/get?format=src&type=jpg", UriKind.Absolute);
    BitmapImage bmi = new BitmapImage();
    bmi.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
    bmi.UriSource = myUri;
    KittyPic.Source = bmi;
}

private void KittyPic_ImageOpened(object sender, RoutedEventArgs e)
{
    LoadingPanel.Visibility = Visibility.Collapsed;
}

private async void KittyPic_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
    LoadingPanel.Visibility = Visibility.Collapsed;
    await new MessageDialog("Failed to load the image").ShowAsync();
}

除了TextBlockProgressBar,您当然可以展示任何您想要的内容,或者例如在两个图像之间进行交换以继续显示旧图片。

对于其他建议,我认为当您熟悉基础知识时,应该查看数据绑定,这非常有帮助且功能强大。此外,请查看有关MVVM模式的文章。


非常感谢您提供的出色答案。比我期望的还要详细。微软有一些很棒的文档和提示可用,但我很难找到它们。我认为它们的布局需要改进。 - rcbevans
我刚刚花了好几个小时尝试跟随那个MVVM模式教程,但其中大部分对于WP8.1来说都不适用,因为命名空间已经改变了。例如,本地应用程序设置的工作方式完全不同,仅仅将命名空间更改为Windows.Storage.Current.LocalSettings是不够的。不过,我还是理解了它的要点。 - rcbevans

2

1)这是正确的做法吗?

不是。您应该采用MVVM模式,将业务逻辑与视图分离 - 意味着您不应在视图的代码后台中创建位图图像或请求/分配此类远程图像URL。您应该在ViewModel中完成这些工作,并将它们绑定到您的View。

因此,在您的情况下,您的ViewModel(实现INotifyPropertyChanged)中将有一个Uri属性(或一个字符串,其中您将分配远程URL),然后在您的View中,您将像这样进行绑定:

<!--TODO: Content should be placed within the following grid-->
    <Grid Grid.Row="1" x:Name="ContentRoot">
         <BitmapImage UriSource="{Binding BackGroundImage}" CreateOptions="BackgroundCreation" />
        <Button HorizontalAlignment="Center" VerticalAlignment="Bottom" x:Name="newPic" Click="newPic_Click" >New Kitty</Button>
    </Grid>

每当您设置BackGroundImage属性时,都会引发一个名为RaisePropertyChanged("BackGroundImage")的事件;这是经典的MVVM方法。
因此,您的视图将意识到BackGroundImage已更改,并自动加载它。(但请注意,如果您只为此BackGroundImage提供字符串,则必须使用转换器,即字符串到Uri转换器,因为它仅接受远程图像的Uri)
2)“...有没有一种设置方式,以便旧图片保留,直到新图片准备好显示,或者在新图片可以替换它之前显示占位符“加载”图像。”
我建议使用显示“加载”图像。我在此处遇到了与您完全相同的问题,我的解决方法是插入一个加载图像,并将其不透明度值设置为0.1 - 与实际图像一起。当您在远程URL之间切换时,当上一个图像消失时,不透明的加载图像出现,当加载下一个实际图像时,加载图像不会显示,因为新图像正在覆盖它。

谢谢你的建议。在CLR应用程序中,MVVM似乎是一个很重要的问题,我会在有时间的时候仔细阅读Johan上面提到的教程。 - rcbevans
我不明白为什么MVVM模式对于Windows Phone应用程序来说是一个很大的问题,因为它完美地解决了许多问题。 - halil
你误解了我的意思,我完全同意,这就是为什么对于Windows手机应用程序来说这是一件大事,因为在Android中,MVVM并不是被推广的东西。经过研究,我完全接受它在Windows开发中的价值,因此我打算通过MSDN上的教程来学习。不同的平台,不同的做事方式,这是有道理的。 - rcbevans

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接