Windows Phone笔记(4)图片操作
图片作为应用程序不可或缺,也是程序中最常见的元素之一,那么在Windows Phone中该如何使用它呢?这就是这篇文章需要解决的问题。在Silverlight支持两个格式的图片显示,它们是:JPEG和PNG,PNG使用的是无损压缩算法,可以准确重建原始位图;而JPEG使用的是一种有损压缩算法,这种算法会对齐人们肉眼不易察觉到的一些可视信息这种压缩方式对照片这样的图像非常有效,但是却不适用于文本或基于矢量派生的位图,如建筑图和动画。
1.Image元素
在Silveright中,Image元素用来显示位图,但是Image元素并不是位图本身。现在我们通过一个简单是示例程序来学习如何在Windows Phone开发中如何使用Image元素。首先新建一个Silverlight for Windows Phone应用程序,在项目根目录下新建文件夹Images,在一张Silverlight支持格式的图片放到这个文件夹中,然后在页面中添加一个Image控件,最后将PhoneApplicationPage标记的SupportedOrientations属性值为PortraitOrLandscape,使程序能够支持横向显示。
下面是显示页面的MainPage.xaml代码:
<Image Name="image1" Source="Images/Superman.jpg"/>
编译运行:
默认的情况,在保持正确的宽高比例的前提之下,Image元素会尽量放大或缩小以充满其容器(内容网格)。如果想要按原始像素尺寸显示图片的话,可以将Image的Stretch属性设置为None:
<Image Name="image1" Source="Images/Superman.jpg" Stretch="None" />
运行效果如下:
2.通过Web获取图像
Image可以通过Web来获取图像,只需要将Image的Source属性设置为一个可用的URL即可,如下面的示例所示:
<Image Name="image1" Source="http://images.cnblogs.com/cnblogs_com/IPrograming/353645/o_sammy-2-anos.jpg" />
运行效果如下:
这样Image元素就可以根据给定的URL从Web中下载图片并显示,这样就能够控制可执行文件的大小,但是考虑到Windows Phone设备并不总是与Web连接,并且在图片下载过程中还可能遇到其他的问题。为了处理这种情况,我们可以根据Image元素的两个事件:ImageFailed(出现错误后发生)和ImageOpened(下载成功后发生),根据这两个事件来判断下载是否成功,从而使程序根据不同的情况作出相应的选择。
3.Image和ImageSource
Image元素还有一些需要我们注意的地方,首先Image元素不是位图,它只是用来显示位图。从前面给出的示例,Image的Source属性被设置为一个相对文件路径或者一个可用URL,从表象上看,我们可以得出Source属性是字符串类型的结论,这并不正确。Source实际上是一个ImageSource类型,这是由于XAML语法隐藏了一些底层的是实现细节。 ImageSource是一个抽象类,派生了BitmapSource,BitmapSource类型是另一个抽象类,他定义了一个名为SetSource的方法,该方法可以通过Stream对象加载图像。派生自BitmapSource的BitmapImage支持一个可接收Uri对象的构造函数,包括一个Uri类型的UriSource属性。
-->>表示派生
ImageSource-->>BitmapSource(SetSource方法,通过Stream对象加载图像)-->>BitmapImage(Uri类型的UriSource属性)
让我们来观察下面的示例程序:
这是MainPage.xmal的代码,添加一个Image元素,且没有设置其Source属性。
<Image Name="image1"/>
这是Mainpage.xmal.cs的触摸事件处理程序:
protected override void OnManipulationStarted(ManipulationStartedEventArgs e)
{
Uri uri = new Uri("http://images.cnblogs.com/cnblogs_com/IPrograming/353645/o_sammy-2-anos.jpg");
BitmapImage bmp = new BitmapImage(uri);
this.image1.Source = bmp;
e.Complete();
e.Handled = true;
base.OnManipulationStarted(e);
}
程序运行,触摸屏幕后就会触发事件,把图片下载到本地显示出来。
我们也可以直接使用WebClient来操作位图,虽然这并不是通用的方法,但我们依然可以做到。
XMAL页面代码和上面的示例一致,这里就不在给出,这是后台处理程序代码:
protected override void OnManipulationStarted(ManipulationStartedEventArgs e)
{
WebClient webClient = new WebClient();
webClient.OpenReadCompleted += OnWebClientReadCompleted;//绑定事件
webClient.OpenReadAsync(new Uri("http://images.cnblogs.com/cnblogs_com/IPrograming/353645/o_sammy-2-anos.jpg"));
e.Complete();
e.Handled = true;//表示事件已处理
base.OnManipulationStarted(e);
}
private void OnWebClientReadCompleted(object sender, OpenReadCompletedEventArgs args)
{
if (!args.Cancelled && args.Error == null)
{
BitmapImage bmp = new BitmapImage();
bmp.SetSource(args.Result);//利用SetSource通过Stream对象创建位图
this.iamge1.Source = bmp;
}
}
4.本地位图加载
前面所有的示例程序中,位图都是作为资源(Resource)添加到项目中,并嵌入可执行文件中,这是最常见的做法。那么如何直接加载本地的位图呢?请看下面的这个示例程序。新建一个项目,包含一个Image元素,和一个Images文件夹,文件夹中存放一张图片。右击图片-->属性-->生成操作-->选择Resource。这是示例的运行效果是:运行程序后触摸屏幕,事件处理程序通过Application类定义的GetResourceStream访问资源。
MainPage.xaml:
<Image Name="image1"/>
MainPage.xmal.cs
protected override void OnManipulationStarted(ManipulationStartedEventArgs e)
{
Uri uri = new Uri("/SilverlightTapToDownload;component/Images/Superman.jpg",UriKind.Relative);//当生成操作为Resource时URL显得比较复杂
StreamResourceInfo resourceInfo = Application.GetResourceStream(uri);
BitmapImage bmp = new BitmapImage();
bmp.SetSource(resourceInfo.Stream);
this.image1.Source = bmp;
e.Complete();
e.Handled = true;
base.OnManipulationStarted(e);
}
我们可以看到URL显得比较复杂,当我们把图片属性的生成操作(Build Action)改为内容(Content)时,语法可以这样简写:
Uri uri = new Uri("Images/Superman.jpg", UriKind.Relative);
我们通过程序Bin目录生成XAP文件后缀改成zip,解压后来分析二者的区别:
①当生成操作为Resource时,图片与编译后的程序一同存储在DLL文件中。
②当生成操作为内容(Content)时,图片存储在DLL文件的外部,但是依旧还早XAP文件中。
二者的使用情境该如何选择呢?微软建议使用在应用程序中将生成操作设置为”内容“,这样可以限制二进制文件(DLL)的大小,缩短启动时间。但是如果这些资源存在于程序所引用的Silverlgiht库中则最好将生成操作设置为“Resource”将其嵌入二进制文件(DLL)中。
5.从相机中获取图片
当用户使用手机拍照后,我们可以通过程序获取用户的照片。要完成这个操作需要使用在Microsoft.Phone.Tasks的命名空间中的选择器(Chooser)类和启动器(Launcher)类,启动器不返回任何数据,选择器有数据返回。
让我们来看下面给出的示例:
MainPage.xmal
<!--ContentPanel - 在此处放置其他内容-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Image Name="image1"/>
</Grid>
这里是完整的后台处理程序:
MainPage.xaml.cs
1 public partial class MainPage : PhoneApplicationPage
2 {
3 CameraCaptureTask camera = new CameraCaptureTask();
4 // 构造函数
5 public MainPage()
6 {
7 InitializeComponent();
8 camera.Completed += OnCameraCapTureTaskCompleted;//建议在构造函数中绑定事件
9 }
10
11 protected override void OnManipulationStarted(ManipulationStartedEventArgs e)
12 {
13 camera.Show();//显示相机程序
14
15 e.Complete();//表示操作已经完成
16 e.Handled = true;//事件完成,停止路由事件转发
17 base.OnManipulationStarted(e);
18 }
19 private void OnCameraCapTureTaskCompleted(object sender, PhotoResult args)
20 {
21 if (args.TaskResult == TaskResult.OK)//选择器操作完成
22 {
23 BitmapImage bmp = new BitmapImage();
24 bmp.SetSource(args.ChosenPhoto);//将照片数据的流赋给bmp对象
25 this.iamge1.Source = bmp;
26 }
27 }
28 }
触摸MainPage页面后会触发手机相机程序,模拟器会模拟一个拍照场景,点击拍照后,选择接受,后程序就会把照片显示在Image元素中,效果如下:
6.从手机媒体库中获取图片
我们可以也通过PhotoChooserTask类来引导用户进入手机图片库并选择照片,然后将结果返回给程序并显示出来。下面就是具体操作的代码:
MainPage.xmal
<!--ContentPanel - 在此处放置其他内容-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Image Name="image1"/>
</Grid>
MainPage.xmal.cs
1 public partial class MainPage : PhoneApplicationPage
2 {
3 PhotoChooserTask photoChooserTask;//声明任务对象。它必须具有页面范围,因此应在页面中的构造函数之前声明它。
4 // 构造函数
5 public MainPage()
6 {
7 InitializeComponent();
8 photoChooserTask = new PhotoChooserTask();//初始化任务对象
9 photoChooserTask.Completed += photoChooserTask_Completed;//标识要在用户完成任务后运行的方法。
10 }
11
12 protected override void OnManipulationStarted(ManipulationStartedEventArgs e)
13 {
14 try
15 {
16 photoChooserTask.Show();//启动任务
17 }
18 catch (System.InvalidOperationException ex)
19 {
20 MessageBox.Show("An error occurred:" + ex.ToString());
21 }
22 e.Complete();
23 e.Handled = true;
24 base.OnManipulationStarted(e);
25 }
26
27 private void photoChooserTask_Completed(object sender, PhotoResult e)
28 {
29 if (e.TaskResult == TaskResult.OK)//选择器操作完成
30 {
31 BitmapImage bmp = new BitmapImage();
32 bmp.SetSource(e.ChosenPhoto);//将照片数据的流赋给bmp对象
33 this.image1.Source = bmp;//将选择的图片显示出来
34 }
35 }
36
37 }
程序运行后,通过触摸MainPage页面就会打开手机图片库,选择好图片之后,程序会负责把图片显示到Image元素中,下面是程序运行效果:
7.图片保存到手机媒体库(MediaLibrary)中
使用手机时,将图片保存到手机的媒体库中也是一种非常常见的操作,在Windows Phone中该如何完成这个操作呢?在Silverlight for Windows Phone并不支持这个操作,我们需要引用XNA库的Microsoft.Xna.Framework.DLL的文件需要使用其中的MedaiLibrary类,下面给出示例代码:
MainPage.xmal
1 <!--ContentPanel - 在此处放置其他内容-->
2 <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
3 <Image Name="image1" Source="Images/Superman.jpg" />
4 <Button Content="保存" Height="72" HorizontalAlignment="Left" Name="btnSave" VerticalAlignment="Bottom" Margin="30 0" Width="160" Click="btnSave_Click" />
5 <Button Content="打开" Height="72" HorizontalAlignment="Right" Name="btnOpen" VerticalAlignment="Bottom" Margin="30 0" Width="160" Click="btnOpen_Click" />
6 </Grid>
MainPage.xmal.cs
1 /// <summary>
2 /// 将图片保存到媒体库中
3 /// </summary>
4 /// <param name="sender"></param>
5 /// <param name="e"></param>
6 private void btnSave_Click(object sender, RoutedEventArgs e)
7 {
8 // 在独立存储中为JPEG文件创建一个文件名。
9 String tempJPEG = "SuperMan.jpg";
10
11 // 创建虚拟存储和文件流,确保这个文件唯一。
12 var store = IsolatedStorageFile.GetUserStoreForApplication();
13 if (store.FileExists(tempJPEG))
14 {
15 store.DeleteFile(tempJPEG);
16 }
17
18 IsolatedStorageFileStream fileStream = store.CreateFile(tempJPEG);
19 StreamResourceInfo sri = null;
20 Uri uri = new Uri("Images/Superman.jpg", UriKind.Relative);
21 sri = Application.GetResourceStream(uri);//获取需要保存文件的流
22
23 BitmapImage bitmap = new BitmapImage();
24 bitmap.SetSource(sri.Stream);
25 WriteableBitmap wbmp = new WriteableBitmap(bitmap);
26
27
28 //将 WriteableBitmap 对象编码成JPEG 流。
29 Extensions.SaveJpeg(wbmp, fileStream, wbmp.PixelWidth, wbmp.PixelHeight, 0, 85);
30 fileStream.Close();
31
32 // 从独立存储中创建一个新的流,并将该JPEG文件存储到Windows Phone的MeidaLibrary中。
33 fileStream = store.OpenFile(tempJPEG, FileMode.Open, FileAccess.Read);
34
35 MediaLibrary mediaLibrary = new MediaLibrary();
36 Picture pic = mediaLibrary.SavePicture("Superman.jpg", fileStream);//将保存在MediaLibrary中的图片命名为:Superman
37 fileStream.Close();//关闭流
38 }
39
40 /// <summary>
41 /// 打开手机MediaLibrary
42 /// </summary>
43 /// <param name="sender"></param>
44 /// <param name="e"></param>
45 private void btnOpen_Click(object sender, RoutedEventArgs e)
46 {
47 PhotoChooserTask photoChooserTask = new PhotoChooserTask();
48 photoChooserTask.Show();//使用照片选择器,打开MediaLibrary
49 }
运行上面的程序后,触摸“保存”按钮就会将显示的图片保存到手机的媒体库中,触摸“打开”会打开手机媒体库,我们可以看见刚才保存的图片,下面是程序的运行实际效果:
参考资料:
http://msdn.microsoft.com/zh-cn/library/cc296240(v=vs.95).aspx
http://msdn.microsoft.com/zh-cn/hh394019(重要)
http://msdn.microsoft.com/zh-cn/hh202973(重要)
http://msdn.microsoft.com/zh-cn/hh394006
http://windowsphonegeek.com/articles/encode-and-decode-images-in-wp7(重要)
《Programming Windows Phone 7 Microsoft Silverlight Edition》
作者:晴天猪
出处:http://www.cnblogs.com/IPrograming
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。