0
回答
LBS/Lens/Sensors (LBS、镜头和传感器应用接口使用)
华为云数据库免费试用   

一、加速计

加速度计用于测量加速度。借助一个三轴加速度计可以测得一个固定平台相对地球表面的运动方向。

不同平台的概述

Android

在运用该API常用到的两个类和一个接口:

SensorManager:传感器管理类。

Sensor:一个描述传感器的类。

SensorEventListener:传感器事件监听类(SensorListener类已过期)。

一般运用步骤:

1. 通过上下文获取SensorManager的对象。

2. 实例化一个你需要使用的sensor对象(也可以通过getSensorList()来获取所有的传感器对象,返回一个list)。

3. 实现传感器监听接口。

4. 注册监听。

5. 反注册监听。

IOS

iPhone内置的加速计是三维全方向感知的,手机平面左右两侧对应加速计x轴负正,手机上下对应y轴正负,垂直手机平面朝里朝外对应z轴正负。当 iPhone静止时,受到的重力加速度为1g,根据摆放位置分摊在三个轴上,比如z轴g = -1说明手机正面朝上平放,而不管怎么放置,三轴方向的加速度都不会超过1g。但当运动状态时,就可能出现特别的数值,正是根据这些数值我们可以判断出运动方向和速度并用于应用中。

Win8

此传感器返回有关 x、 y 和 z 轴的 G 力值。具体步骤大致如下:

1. 获取加速计对象。

2. 注册 ReadingChanged 事件(或Shaken事件)进行处理。

3. 反注册ReadingChanged 事件(或Shaken事件)。

WP8

加速度计测量在某一时刻施加于设备的力。可以使用这些力来确定用户正在向哪个方向移动设备。加速度值采用 3 维矢量表示,该矢量表示在 X、Y 和 Z 轴中的加速度分量(采用重力单位)。当设备面朝平台时,加速度的方向相对于设备以便对 Z 轴应用 -1g,当垂直于平台顶部放置设备时,对 Y 轴应用 -1g。

加速度计传感器检测重力以及由于手机运动而产生的任何力。使用 Motion 类访问的组合运动 API 使用多个设备传感器将重力矢量与设备加速度分离,并且允许您轻松确定设备的当前属性(yaw、pitch、roll)。

获取传感器对象

Android

1. 实现传感器监听接口:

public class TestAccel implements SensorEventListener{
public interface onShakeListener{
public void onShake();
}
}

2. 通过上下文获取SensorManager的对象:

sensorManager = Context.getSystemService(Context.Sensor_service);

3. 实例化一个你需要使用的sensor对象:

Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

IOS

UIAccelerometer类是单独存在的。要获取对此类的引用,请调用sharedAccelerometer方法,如下所示。

UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer];

Win8

使用GetDefault方法建立到加速计的连接。如果未找到集成加速计,则方法返回一个 null 值。

Accelerometer accelerometer = Accelerometer.GetDefault();

WP8

声明一个类型为 Accelerometer 的变量。

Accelerometer accelerometer = new Accelerometer();

获取数据

Android

if (sensor != null) {
this.sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_GAME);
}

2. 反注册监听

if (sensorManager != null)
sensorManager.unregisterListener(this);

3. 判断数据

public void onSensorChanged(SensorEvent event) {
long curTime = java.lang.System.currentTimeMillis();
if ((curTime - lastTime) > 10) {
long diffTime = (curTime - lastTime);
lastTime = curTime;
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
float speed = Math.abs(x + y + z - last_x - last_y - last_z) / diffTime * 10000;
if (speed > SHAKE_SHRESHOLD) {
// 检测到摇晃后执行的代码
shakeListener.onShake();
}
last_x = x;
last_y = y;
last_z = z;
}
}

IOS

委托方法:- (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration中的UIAcceleration是表示加速度类。包含了来自加速计UIAccelerometer的真是数据。它有3个属性的值x、y、z。iphone的加速计支持最高以每秒100次的频率进行轮询。此时是60次。

accelerometer.delegate = self;
accelerometer.updateInterval = 1.0/60.0;

Accelerometer类有2个属性:

1. MinimumReportInterval(只读):得到加速计最低报告间隔。

2. ReportInterval(读/写):设置或者获取当前加速度计的报告间隔。

Accelerometer类有2个事件:

1. ReadingChanged:发生在每次传感器更新一组加速度数据。

2. Shaken:发生在加速度计检测到电脑已经动摇了。

accelerometer.ReadingChanged += new TypedEventHandler(ReadingChanged);
accelerometer.Shaken += new TypedEventHandler(Shaken);

获取X,Y和Z的值:

1. 在通过ReadingChanged中,使用e.Reading对象中的3个只读属性AccelerationX,AccelerationY,AccelerationZ来判断3个方向对应的值。通过e.Reading的只读属性Timestamp,可以获得数据的对应的时间戳。

async private void ReadingChanged(object sender, AccelerometerReadingChangedEventArgs e)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
AccelerometerReading reading = e.Reading;
ScenarioOutput_X.Text = String.Format("{0,5:0.00}", reading.AccelerationX);
ScenarioOutput_Y.Text = String.Format("{0,5:0.00}", reading.AccelerationY);
ScenarioOutput_Z.Text = String.Format("{0,5:0.00}", reading.AccelerationZ);
});
}

2. 通过e.Reading的只读属性Timestamp,可以获得数据更新的对应的时间戳。

async private void Shaken(object sender, AccelerometerShakenEventArgs e)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
_shakeCount++;
ScenarioOutputText.Text = “摇动次数:” + _shakeCount + “次”;
});
}

WP8

1. 为 CurrentValueChanged 事件设置处理程序。将以下代码粘贴到空的“开始”按钮单击处理程序中。

if (accelerometer == null)
{
// Instantiate the Accelerometer.
accelerometer = new Accelerometer();
accelerometer.TimeBetweenUpdates = TimeSpan.FromMilliseconds(20);
accelerometer.CurrentValueChanged += new EventHandler

2. 实现 CurrentValueChanged 事件处理程序。具有新加速度计数据的系统会以使用TimeBetweenUpdates 指定的频率调用该方法。该处理程序接收包含加速度计数据的AccelerometerReading 对象。在对 UI 没有访问权限的后台线程上调用该处理程序。因此,该事件处理程序使用 Dispatcher.Invoke 方法,该方法在 UI 线程上调用指定的代码。Dispatcher.Invoke 用于调用将在下一步中定义的 UpdateUI 并传递 AccelerometerReading 对象。

void accelerometer_CurrentValueChanged(object sender, SensorReadingEventArgse)
{
// Call UpdateUI on the UI thread and pass the AccelerometerReading.
Dispatcher.BeginInvoke(() => UpdateUI(e.SensorReading));
}

3. 实现将向用户显示加速度计数据的 UpdateUI 方法。此方法首先更新状态 TextBlock 以指示正在接收数据。接下来,更新三个 TextBlock 对象以显示传感器每个轴上的加速度数值。最后,更新 Line 对象以采用图形的形式演示加速度。

private void UpdateUI(AccelerometerReading accelerometerReading)
{
statusTextBlock.Text = "getting data";
Vector3 acceleration = accelerometerReading.Acceleration;
// Show the numeric values.
xTextBlock.Text = "X: " + acceleration.X.ToString("0.00");
yTextBlock.Text = "Y: " + acceleration.Y.ToString("0.00");
zTextBlock.Text = "Z: " + acceleration.Z.ToString("0.00");
// Show the values graphically.
xLine.X2 = xLine.X1 + acceleration.X * 200;
yLine.Y2 = yLine.Y1 - acceleration.Y * 200;
zLine.X2 = zLine.X1 - acceleration.Z * 100;
zLine.Y2 = zLine.Y1 + acceleration.Z * 100;
}

4. 最后一步是实现将允许用户停止从加速度计获取数据的“停止”按钮单击处理程序。再次,如果编辑器自动添加了此处理程序,则将此处理程序的内容替换为以下内容。

private void stopButton_Click(object sender, RoutedEventArgs e)
{
if (accelerometer != null)
{
// Stop the accelerometer.
accelerometer.Stop();
statusTextBlock.Text = "accelerometer stopped.";
}
}

二、  陀螺仪

陀螺仪能够测出绕轴旋转的角速率值。

不同平台的概述

Android

在运用该API常用到的两个类和一个接口:

SensorManager:传感器管理类。

Sensor:一个描述传感器的类。

SensorEventListener:传感器事件监听类(SensorListener类已过期)。

一般运用步骤:

1. 通过上下文获取SensorManager的对象。

2. 实例化一个你需要使用的sensor对象(也可以通过getSensorList()来获取所有的传感器对象,返回一个list)。

3. 实现传感器监听接口。

4. 注册监听。

5. 反注册监听。

Win8

此传感器返回有关 x、 y 和 z 轴的角速度值。具体步骤大致如下:

1. 获取加速计对象。

2. 注册 ReadingChanged 事件进行处理。

3. 反注册ReadingChanged 事件。

WP8

陀螺仪传感器测量设备沿着其三个主轴的旋转速度。当设备静止时,所有轴的陀螺仪读数都为零。如果设备面向您围绕其中心点旋转,就像飞机螺旋桨一样,那么 Z 轴上的旋转速度值将大于零,设备旋转的速度越快,该值越大。旋转速度的测量以弧度/秒为单位,其中 2 * Pi 弧度就是全程旋转。

如果您想确定设备在空间的绝对方向(yaw、pitch、roll),我们建议您使用组合运动 API,可以使用 Motion 类访问此 API。有关更多信息,请参见如何使用 Windows Phone 的组合运动 API

获取传感器对象

Android

1. 实现传感器监听接口

public class TestGryo implements SensorEventListener{
public interface onShakeListener{
public void onShake();
}
}

2. 通过上下文获取SensorManager的对象。

sensorManager = Context.getSystemService(Context.Sensor_service);

3. 实例化一个你需要使用的sensor对象。

Sensor gyro = sensorManager.getDefaultSensor(Sensor. TYPE_GYROSCOPE);

Win8

使用 GetDefault 方法建立到陀螺仪的连接。如果未找到集成陀螺仪,则方法返回一个 null 值。

Gyrometer gyrometer = Gyrometer.GetDefault();

WP8

声明Gyroscope 类型的对象

Gyroscope gyroscope = new Gyroscope();

获取数据

Android

1. 注册传感器

if (sensor != null) {
this.sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_GAME);
}

2. 反注册传感器

if (sensorManager != null)
sensorManager.unregisterListener(this);

3. 数据判断

public void onSensorChanged(SensorEvent event) {
long curTime = java.lang.System.currentTimeMillis();
if ((curTime - lastTime) > 10) {
long diffTime = (curTime - lastTime);
lastTime = curTime;
magnetic.x += event.data[0] * dT;
magnetic.y += event.data[1] * dT;
magnetic..z += event.data[2] * dT;

float azimuth = atan2((float)(data->magnetic.x ), (float)(data->magnetic.y));
float roll = atan2((float)(data->magnetic.x ), (float)(data->magnetic.z))*180/PI;

if(…){
 //todo
}
}
}

Win8

Gyrometer类有2个属性:

1. MinimumReportInterval(只读):得到陀螺仪最低报告间隔。

2. ReportInterval(读/写):设置或者获取当前陀螺仪的报告间隔。

Gyrometer类有1个事件:

1. ReadingChanged:发生在每次传感器更新一组加速度数据。

gyrometer.ReadingChanged += new TypedEventHandler(ReadingChanged);

判断数据:

在通过ReadingChanged中,使用e.Reading对象中的3个只读属性AngularVelocityX, AngularVelocityY, AngularVelocityZ来判断3个方向对应的值。通过e.Reading的只读属性Timestamp,可以获得数据的对应的时间戳。

async private void ReadingChanged(object sender, GyrometerReadingChangedEventArgs e)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        GyrometerReading reading = e.Reading;
        ScenarioOutput_X.Text = String.Format("{0,5:0.00}", reading.AngularVelocityX);
        ScenarioOutput_Y.Text = String.Format("{0,5:0.00}", reading.AngularVelocityY);
        ScenarioOutput_Z.Text = String.Format("{0,5:0.00}", reading.AngularVelocityZ);
});
}

WP8

1. 定义一个计时器

timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(60);
timer.Tick += new EventHandler(timer_Tick);

2. 为陀螺仪具有新数据时引发的 CurrentValueChanged 事件添加一个事件处理程序。将该代码粘贴到按钮单击处理程序中,放置在之前的代码部分之后。

if (gyroscope == null)
{
// Instantiate the Gyroscope.
gyroscope = new Gyroscope();
// Specify the desired time between updates. The sensor accepts
// intervals in multiples of 20 ms.
gyroscope.TimeBetweenUpdates = TimeSpan.FromMilliseconds(20);
// The sensor may not support the requested time between updates.
// The TimeBetweenUpdates property reflects the actual rate.
timeBetweenUpdatesTextBlock.Text = "time between updates: " + gyroscope.TimeBetweenUpdates.TotalMilliseconds + " ms";
gyroscope.CurrentValueChanged += new EventHandler

3. 使用 Start() 方法启动陀螺仪。调用 Start 有可能会失败,因此您应该将此调用放置在一个 try 块中。在 catch 块中,您可以警告用户陀螺仪可能无法启动。然后启动 计时器timer。

try
{
statusTextBlock.Text = "starting gyroscope.";
gyroscope.Start();
timer.Start();
}
catch (InvalidOperationException ex)
{
statusTextBlock.Text = "unable to start gyroscope.";
}

4. 实现 CurrentValueChanged 事件处理程序。具有新陀螺仪数据的系统会以使用 TimeBetweenUpdates 指定的频率调用该方法。该处理程序接收包含陀螺仪数据的 GyroscopeReading 对象。在对 UI 没有访问权限的后台线程上调用该处理程序。因此,如果您想通过该方法修改 UI,则必须使用 Dispatcher.Invoke 方法在 UI 线程上调用指定的代码。

void gyroscope_CurrentValueChanged(object sender, SensorReadingEventArgse)
{
// Note that this event handler is called from a background thread
// and therefore does not have access to the UI thread. To update 
// the UI from this handler, use Dispatcher.BeginInvoke() as shown.
// Dispatcher.BeginInvoke(() => { statusTextBlock.Text = "in CurrentValueChanged"; });

isDataValid = gyroscope.IsDataValid;

if (lastUpdateTime.Equals(DateTimeOffset.MinValue))
{
// If this is the first time CurrentValueChanged was raised,
// only update the lastUpdateTime variable.
lastUpdateTime = e.SensorReading.Timestamp;
}
else
{
// Get the current rotation rate. This value is in 
// radians per second.
currentRotationRate = e.SensorReading.RotationRate;

// Subtract the previous timestamp from the current one
// to determine the time between readings
TimeSpan timeSinceLastUpdate = e.SensorReading.Timestamp - lastUpdateTime;

// Obtain the amount the device rotated since the last update
// by multiplying by the rotation rate by the time since the last update.
// (radians/second) * secondsSinceLastReading = radiansSinceLastReading
cumulativeRotation += currentRotationRate * (float)(timeSinceLastUpdate.TotalSeconds);

lastUpdateTime = e.SensorReading.Timestamp;
}
}

5. 实现将向用户显示陀螺仪数据的 DispatcherTimer Tick 事件处理程序。此方法首先更新状态 TextBlock 以指示正在接收数据。接下来,更新 TextBlock 对象以显示围绕传感器每个轴的旋转加速度数值以及累积旋转。最后,更新 Line 对象以采用图形的形式演示加速度和旋转。

void timer_Tick(object sender, EventArgs e)
{
if (isDataValid)
{
statusTextBlock.Text = "receiving data from gyroscope.";
}
currentXTextBlock.Text = currentRotationRate.X.ToString("0.000");
currentYTextBlock.Text = currentRotationRate.Y.ToString("0.000");
currentZTextBlock.Text = currentRotationRate.Z.ToString("0.000");
cumulativeXTextBlock.Text =
MathHelper.ToDegrees(cumulativeRotation.X).ToString("0.00");
cumulativeYTextBlock.Text =
MathHelper.ToDegrees(cumulativeRotation.Y).ToString("0.00");
cumulativeZTextBlock.Text =
MathHelper.ToDegrees(cumulativeRotation.Z).ToString("0.00");
double centerX = cumulativeGrid.ActualWidth / 2.0;
double centerY = cumulativeGrid.ActualHeight / 2.0;
currentXLine.X2 = centerX + currentRotationRate.X * 100;
currentYLine.X2 = centerX + currentRotationRate.Y * 100;
currentZLine.X2 = centerX + currentRotationRate.Z * 100;
cumulativeXLine.X2 = centerX - centerY * Math.Sin(cumulativeRotation.X);
cumulativeXLine.Y2 = centerY - centerY * Math.Cos(cumulativeRotation.X);
cumulativeYLine.X2 = centerX - centerY * Math.Sin(cumulativeRotation.Y);
cumulativeYLine.Y2 = centerY - centerY * Math.Cos(cumulativeRotation.Y);
cumulativeZLine.X2 = centerX - centerY * Math.Sin(cumulativeRotation.Z);
cumulativeZLine.Y2 = centerY - centerY * Math.Cos(cumulativeRotation.Z);
}

三、  位置传感器

位置传感器通常用来决定一个设备在世界参考坐标的物理位置。

不同平台的概述

Android

在运用该API常用到的两个类和一个接口:

SensorManager:传感器管理类。

Sensor:一个描述传感器的类。

SensorEventListener:传感器事件监听类(SensorListener类已过期)。

一般运用步骤:

1. 通过上下文获取SensorManager的对象。

2. 实例化一个你需要使用的sensor对象(也可以通过getSensorList()来获取所有的传感器对象,返回一个list)。

3. 实现传感器监听接口。

4. 注册监听。

5. 反注册监听。

IOS

iPhone SDK提供了三个类来管理位置信息:CLLocation CLLocationManager 和 CLLHeading(不常用)。除了使用GPS来获取当前的位置信息外,iPhone也可以基于WiFi基站和无线发射塔来获得位置信息。GPS的精度最高,可以精确到米级别,但是也最耗电。

1. 启动定位服务

2. 获得位置信息

Win8

本部分包括一些有关检测用户地理位置的操作方法主题和指南。有关使用位置和地图的详细信息,请Bing 地图 SDK 样例

WP8

在 Windows Phone 8 中,您可以创建利用手机的物理位置的应用程序。位置感知应用程序的方案包括使用用户的即时位置将用户记入 Web 服务,以及跟踪一段时间内用户位置的变化。手机提供的位置数据来自多个来源,包括 GPS、Wi-Fi 和移动电话。有两组不同的 API 可用于将位置数据引入应用程序。本主题将帮助您决定哪个位置 API 适合您的应用程序。

Windows Phone 运行时 位置 API 在 Windows Phone 8 中首次出现。它具有以下功能:

● 可以从托管代码和本机代码访问。如果您正在编写 Direct3D 应用 应用程序,您必须使用此位置 API。如果您正在创建托管的应用,同样建议您使用此 API。

● 对单触发位置获取的更多支持。API 允许应用指定所需的位置结果精度以及获取结果的所需最长时间,以便平衡精度和响应速度。当不需要跟踪时,建议应用程序使用单触发位置。这有利于电池寿命,从而提供更好的用户体验。

● 当跟踪位置时,应用可以请求在特定时间间隔后或在设备从先前位置移动至少指定距离后接收位置更新。

● 与 Windows 8 的交汇。尽管两个平台之间有细微不同,如果同时面向手机和桌面进行开发,此 API 将允许您重复使用大部分的位置代码。

获取传感器对象

Android

1. 实现传感器监听接口

public class TestGryo implements SensorEventListener{
public interface onShakeListener{
public void onShake();
}
}

2. 通过上下文获取SensorManager的对象。

sensorManager = Context.getSystemService(Context.Sensor_service);

3. 实例化一个你需要使用的sensor对象。

Sensor gyro = sensorManager.getDefaultSensor(Sensor. TYPE_ORIENTATION);

IOS

启动定位服务

CLLocationManager *locManager = [[CLLocationManager alloc] init];
locManager.delegate = self;
[locManager startUpdatingLocation];

Win8

Geoposition pos = await geo.GetGeopositionAsync();

WP8

Geolocator geolocator = new Geolocator();

获取用户位置

Android

1. 注册传感器

if (sensor != null) {
this.sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_GAME);
}

2. 反注册传感器

if (sensorManager != null)
sensorManager.unregisterListener(this);

3. 实现onSensorChanged事件

public void onSensorChanged(SensorEvent event) {
float azimuth_angle = event.values[0];
float pitch_angle = event.values[1];
float roll_angle = event.values[2];
}

IOS

-(void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation fromLocation: (CLLocation *)oldLocation
{
NSTimeInterval howRecent = [newLocation.timestamp timeIntervalSinceNow];
if(howRecent < -10) return ; //离上次更新的时间少于10秒
if(newLocation.horizontalAccuracy > 100) return; //精度> 100米
//经度和纬度
double lat = newLocation.coordinate.latitude;
double lon = newLocation.coordinate.longitude;
}

Win8

1. 打开 Microsoft Visual Studio 2012

打开 Visual Studio 2012。

2. 创建一个新项目

创建新项目,从“Visual C#”>“Windows 应用商店”项目类型中选择一个“应用程序”。

3.  在应用中插入 C# 代码

打开你的项目的 MainPage.xaml.cs 文件并将现有的代码替换为下面的代码。

using System;
using System.Collection.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.DirectUI;
using Windows.UI.DirectUI.Controls;
using Windows.UI.DirectUI.Data;
using Windows.Devices.Geolocation;

namespace GeolocationSample
{
partial class MainPage
{
Geolocator geo = null;
public MainPage()
{
InitializeComponent();
}
private async void button1_Click(object sender, RoutedEventArgs e)
{
if (geo == null)
{
geo = new Geolocator();
}
Geoposition pos = await geo.GetGeopositionAsync();            
textblockLatitude.Text = "Latitude: " + pos.Coordinate.Latitude.ToString();
textblockLongitude.Text = "Longitude: " + pos.Coordinate.Longitude.ToString();
textblockAccuracy.Text = "Accuracy: " + pos.Coordinate.Accuracy.ToString();
}
}
}

你将需要用你的应用的类名称替换上面的代码片段中的类名称。例如,如果你创建了一个名为“Application1”的项目,则需要将:

namespace GeolocationSample

替换为

namespace Application1

4. 在应用中插入 XAML 代码

打开名为 MainPage.xaml 的文件,然后将下面的 XML 复制到此文件中(替换原来的内容)。

<UserControl x:Class="GeolocationSample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="768" d:DesignWidth="1366">
<Grid x:Name="LayoutRoot" Background="Gray">
<Button Content="Get Location Async" Click="button1_Click" Height="23" 
HorizontalAlignment="Left" Margin="12,180,0,0" Name="button3" VerticalAlignment="Top" Width="120" />
<TextBlock Margin="14,220,0,0" Text="Latitude" Name="textLatitude" />
<TextBlock Margin="14,260,0,0" Text="Longitude" Name="textLongitude" />
<TextBlock Margin="14,300,0,0" Text="Accuracy" Name="textAccuracy" />
</Grid>
</UserControl>

你将需要用你的应用的类名称替换上面的代码片段中类名称的第一部分。例如,如果你创建了一个名为“Application1”的项目,则需要将:

<UserControl x:Class="GeolocationSample.MainPage"

替换为

<UserControl x:Class="Application1.MainPage"

5. 生成应用

选择“生成”>“生成解决方案”以生成项目。

6. 测试应用

1) 在“调试”菜单上,单击“开始调试”测试该解决方案。

2) 首次运行该示例时,你会收到一个提示,询问是否可以让应用使用你的位置数据。选择“允许”选项。

3) 单击“获取位置”按钮获取当前的位置。

注意  如果位置数据未显示,则检查以下内容:

● 确保已启用对位置的访问权限,方法是在解决方案资源管理器中打开 package.appxmanifest 并在“功能”选项卡中选中“位置”。

● 如果管理员已禁用定位服务,则你的应用将无法访问用户的位置。在桌面控制面板中,打开“更改位置设置”并检查是否已选中“打开 Windows 位置平台”。

前面的示例说明,只需编写少量代码即可在应用中集成地理位置功能。

应用会在 button1_Click 方法中发出获取地理位置信息的异步请求。这通过下面一行实现:

Geoposition pos = await geo.GetGeopositionAsync();

后面几行将捕获新的地理位置数据:

textblockLatitude.Text = "Latitude: " + pos.Coordinate.Latitude.ToString();
textblockLongitude.Text = "Longitude: " + pos.Coordinate.Longitude.ToString();
textblockAccuracy.Text = "Accuracy: " + pos.Coordinate.Accuracy.ToString();

以下新值将写入项目 XAML 中的三个 TextBlock 中。

<TextBlock Margin="14,220,0,0" Text="Latitude" Name="textLatitude" />
<TextBlock Margin="14,260,0,0" Text="Longitude" Name="textLongitude" />
<TextBlock Margin="14,300,0,0" Text="Accuracy" Name="textAccuracy" /> 

WP8

1.  在 Visual Studio 中创建新的 Windows Phone 应用。

2. 在“解决方案资源管理器”中,展开“属性”文件夹,然后双击 WMAppManifest.xml。

3. 在清单设计器的“功能”选项卡上,选中“ID_CAP_LOCATION”旁边的复选框。

4. 在 MainPage.xaml 中,将下列 XAML 代码粘贴到现有的名为 ContentPanel 的 Grid 元素上。此代码定义一个将启动位置 API 的按钮,以及一些文本块来显示维度、经度和应用的状态。

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel>
<Button x:Name="OneShotLocationButton" Click="OneShotLocation_Click" Content="get one-shot location"/>
<TextBlock x:Name="LatitudeTextBlock"/>
<TextBlock x:Name="LongitudeTextBlock"/>
<TextBlock x:Name="StatusTextBlock"/>
</StackPanel>
</Grid>

5. 在 MainPage.xaml.cs 文件的顶部添加以下 using 语句。

using System.Threading.Tasks;
using Windows.Devices.Geolocation;

6. 添加同意提示以允许用户选择不让您的应用访问其位置。所有应用在使用位置 API 之前,应取得用户同意。本例检查 MainPage 类的 OnNavigatedTo(NavigationEventArgs) 方法中的用户同意,只要应用启动,就会调用它。代码首先检查 ApplicationSettings 字典,以了解是否存在“LocationConsent”密钥。如果发现该密钥,则意味着用户已经选择或退出位置,因此该方法立即返回。如果未发现该密钥,那么将显示 MessageBox,寻求用户同意。MessageBox 操作的结果受到检查,指示用户同意状态的布尔值存储在 ApplicationSettings 中的“LocationConsent”密钥内。在应用尝试访问用户位置之前,此密钥将受到检查。

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
if (IsolatedStorageSettings.ApplicationSettings.Contains("LocationConsent"))
{
// User has opted in or out of Location
return;
}
else
{
MessageBoxResult result = 
MessageBox.Show("This app accesses your phone's location. Is that ok?", "Location",MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
{
IsolatedStorageSettings.ApplicationSettings["LocationConsent"] = true;
}else
{
IsolatedStorageSettings.ApplicationSettings["LocationConsent"] = false;
}
IsolatedStorageSettings.ApplicationSettings.Save();
}
}

7. 将下列处理程序粘贴到用于按钮单击事件的 MainPage.xaml.cs 中。该方法首先检查 ApplicationSettings 字典中用户同意的状态。如果值为 false,则该方法立即退出。一旦确定用户同意,则该方法初始化 Geolocator 对象,并设置 DesiredAccuracyInMeters 属性。随后,将调用 GetGeopositionAsync 方法。此方法尝试获取手机的当前位置。此为异步操作,因此在获取位置时不会阻止 UI 线程。您可以使用 await 操作符将代码置于异步调用之后,将在调用完成后执行。这需要该处理程序方法被申明为 async。因为可以确保使用 await 发起的调用返回在调用开始的线程上,而且 await 调用从 UI 线程发起,因此代码可以在调用返回时,直接访问并修改 UI。

整个位置操作都包装在 try 块中,以防止引发任何异常。如果在开发时引发 UnauthorizedAccessException 异常,可能意味着您的应用清单中未包含 ID_CAP_LOCATION。如果在已经部署应用之后发生这种情况,则可能意味着用户已在手机“设置”中禁用了此应用的位置。

private async void OneShotLocation_Click(object sender, RoutedEventArgs e)
{
if ((bool)IsolatedStorageSettings.ApplicationSettings["LocationConsent"] != true)
{
// The user has opted out of Location.
return;
}
Geolocator geolocator = new Geolocator();
geolocator.DesiredAccuracyInMeters = 50;
try
{
Geoposition geoposition = await geolocator.GetGeopositionAsync(
maximumAge: TimeSpan.FromMinutes(5),
timeout: TimeSpan.FromSeconds(10)
);
LatitudeTextBlock.Text = geoposition.Coordinate.Latitude.ToString("0.00");
LongitudeTextBlock.Text = geoposition.Coordinate.Longitude.ToString("0.00");
}
catch (Exception ex)
{
if ((uint)ex.HResult == 0x80004004)
{
// the application does not have the right capability or the location master switch is off
StatusTextBlock.Text = "location  is disabled in phone settings.";
}
//else
{
// something else happened acquring the location
}
}
}

四、  摄像头

可以有效支持设备上的各种照相机和相机功能。

不同平台的概述

Android

对于在Android手机系统上进行Camera的开发,我们可以使用两类方法

1. 借助Intent和MediaStroe调用系统Camera App程序来实现拍照和摄像功能。

2. 根据Camera API自写Camera程序。

注:本文主要讲述第一种方式。

IOS

想要将摄像头进行视频录制或者拍照可以用UIImagePickerController,不过UIImagePickerController会弹出一个自己的界面,可是有时候我们不想要弹出的这个界面,那么就可以用另一种方法来获取摄像头得到的数据了。

Win8

在使用 C++、C# 或 Visual Basic 的 Windows 应用商店应用中,用来捕获、播放和呈现媒体的控件是 CaptureElementMediaElement 和 Image。 这些控件是在Windows.UI.Xaml.Controls 命名空间中定义的。

对于有关在使用 C++、C# 或 Visual Basic 的 Windows 应用商店应用中使用音频和视频的性能指南,请参阅优化媒体资源

WP8

在Windows Phone中有两组 API 可供您的应用使用。

说明

在 Windows Phone OS 7.1 上是否受支持

在 Windows Phone 8 上是否受支持

PhotoCamera 使用此类来捕获照片和访问预览缓冲区。

green check mark

green check mark

PhotoCaptureDevice 使用此类以用于高级相片捕获和访问预览缓冲区。

red x

green check mark

Microsoft.Devices.PhotoCamera 类提供以编程方式捕获照片的功能。您可以使用此类指定照片分辨率和闪光灯设置并触发自动对焦。

对于高级照片捕获,Windows.Phone.Media.Capture.PhotoCaptureDevice 类提供一组扩展的功能和更好的性能。使用此类控制照片属性。

注:本文主要介绍PhotoCamera的使用方法。

本主题涉及以下步骤:

1. 创建相机 UI

2. 实现基于取景器和相机的事件

3. 保存到媒体库和本地文件夹

创建相机

Android

准备工作

这里用Camera API,就必须在manifest内声明使用权限,通常由以下三项

<uses-permission android:name = "android.permission.CAMERA" /> 
<uses-feature android:name = "android.hardware.camera" /> 
<uses-feature android:name = "android.hardware.camera.autofocus" /> 

一般拍照和摄像的时候需要写到sd卡上,所以还有一向权限声明如下

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 

真做摄像功能时,需要音频录制和视频录制功能,所以又需要下面两项权限声明

<uses-permission android:name="android.permission.RECORD_VIDEO"/> 
<uses-permission android:name="android.permission.RECORD_AUDIO"/>  

另外使用Camera API拍照或摄像,都需要用到预览,预览就要用到SurfaceView,为此Activity的布局中必须有SurfaceView。

拍照流程

1. 在Activity的OnCreate函数中设置好SurfaceView,包括设置SurfaceHolder.Callback对象和SurfaceHolder对象的类型,具体如下

SurfaceView mpreview = (SurfaceView) this.findViewById(R.id.camera_preview); 
SurfaceHolder mSurfaceHolder = mpreview.getHolder(); 
mSurfaceHolder.addCallback(this); 
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

2. 在SurfaceHolder.Callback的surfaceCreated函数中,使用Camera的Open函数开机摄像头硬件,这个API在SDK 2.3之前,是没有参数的,2.3以后支持多摄像头,所以开启前可以通过getNumberOfCameras先获取摄像头数目,再通过getCameraInfo得到需要开启的摄像头id,然后传入Open函数开启摄像头,假如摄像头开启成功则返回一个Camera对象,否则就抛出异常;

IOS

首先需要引入一个包#import <AVFoundation/AVFoundation.h>,接下来你的类需要实现AVCaptureVideoDataOutputSampleBufferDelegate这个协议,只需要实现协议中的一个方法就可以得到摄像头捕获的数据了。

- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection
{
// Create a UIImage from the sample buffer data
UIImage *image = [self imageFromSampleBuffer:sampleBuffer];
mData = UIImageJPEGRepresentation(image, 0.5);
}

Win8

1. 在应用部件清单 (manifest) 中设置设备功能

若要启用摄像机访问,应用必须在应用清单中包含 Webcam DeviceCapability

1) 在 Microsoft Visual Studio Express 2012 for Windows 8 的解决方案资源管理器中,通过双击 package.appxmanifest 项打开应用程序清单设计器。

2) 单击“功能”。

3) 选中“摄像机”。

2. 创建 CaptureElement

使用XAML创建 CaptureElement。并使它呈现到显示器上。

<CaptureElement Name="capturePreview" Height="400" />

3. 创建 MediaCapture 对象。

MediaCapture 类包含用于管理所捕获视频的方法和属性。 要从相机捕获和旋转视频,你需要使用 InitializeAsyncStartPreviewAsync 和 SetPreviewRotation。使用SetRecordRotation 可为正在录制的视频设置旋转。

实例化一个新的 MediaCapture对象,并调用 InitializeAsync 以初始化 MediaCapture 对象为默认设置。 你可以将 MediaCaptureInitializationSettings对象传入 InitializeAsync 以设置特定设置。

MediaCapture captureMgr = new MediaCapture();
await captureMgr.InitializeAsync();

WP8

创建相机 UI 的步骤

1. 若使用 Windows Phone SDK,则使用 Windows Phone 应用 模板创建新项目。

2. 创建项目后,从“项目”菜单中,选择“添加引用”。在 .NET 标签上,选择 Microsoft.XNA.Framework,然后单击“确定”。

3. 在 MainPage.xaml 文件中,更新 phone:PhoneApplicationPage 元素,如以下代码中所示。

SupportedOrientations="Landscape" Orientation="LandscapeLeft" shell:SystemTray.IsVisible="False"

这将配置横向的页面以及隐藏系统托盘。

4. 在 MainPage.xaml 中,将名为 LayoutRoot 的 Grid 替换为以下代码。

<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="640" />
<ColumnDefinition Width="160" />
</Grid.ColumnDefinitions>
<Canvas x:Name="viewfinderCanvas" Width="640" Height="480" 
                   HorizontalAlignment="Left" >
<!--Camera viewfinder -->
<Canvas.Background>
<VideoBrush x:Name="viewfinderBrush" />
</Canvas.Background>
</Canvas>
<!--Button StackPanel to the right of viewfinder>-->
<StackPanel Grid.Column="1" >
<Button x:Name="ShutterButton" Content="SH" Click="ShutterButton_Click" FontSize="26" FontWeight="ExtraBold" Height="75" />
</StackPanel>
<!--Used for debugging >-->
<TextBlock Height="40" HorizontalAlignment="Left" Margin="8,428,0,0" Name="txtDebug" VerticalAlignment="Top" Width="626" FontSize="24" FontWeight="ExtraBold" />
</Grid>

该代码创建一个 640 x 480 取景器区域,该区域具有一个包含快门按钮 SH 的StackPanel控件。在以下部分中实现 ShutterButton_Click 事件。

说明:本练习中的软件快门按钮只是为了演示采用编程方式访问 PhotoCamera API。若要优化用户的体验,我们建议您的应用使用相机上的硬件快门按钮。有关如何实现硬件快门按钮的信息,请参见如何访问 Windows Phone 中的硬件相机快门按钮

5. 打开主页的代码隐藏文件 MainPage.xaml.cs,在该页面的顶部添加以下指令。

// Directives
using Microsoft.Devices;
using System.IO;
using System.IO.IsolatedStorage;
using Microsoft.Xna.Framework.Media;

6. 在 MainPage.xaml.cs 的 MainPage 类中,在 MainPage 类的构造函数前面添加以下变量声明。

// Variables
private int savedCounter = 0;
PhotoCamera cam;
MediaLibrary library = new MediaLibrary();

7. 若要创建相机应用程序,必须在应用清单文件中声明相机功能。如果没有相机功能,该应用将无法工作。打开 WMAppManifest.xml 并确认存在以下功能元素。

<Capability Name="ID_CAP_ISV_CAMERA"/>

如果您的 Windows Phone 8 应用要求具有背面相机,则使用 ID_REQ_BACKCAMERA 硬件要求防止用户在没有背面相机的情况下下载您的应用。有关功能和要求的更多信息,请参见 Windows Phone 应用的功能和硬件要求

8. 要访问媒体库,必须在应用清单文件中声明媒体库功能。若没有该功能,应用无法将文件保存到媒体库。根据应用针对的 Windows Phone 版本,添加以下功能:

● ID_CAP_MEDIALIB:Windows Phone OS 7.1 应用可以使用此功能访问媒体库。

● ID_CAP_MEDIALIB_PHOTO:Windows Phone 8 应用可以使用此功能将照片保存到媒体库。

Windows Phone 8 应用需要使用其他功能访问媒体库中的视频和音频。有关更多信息,请参见 Windows Phone 应用的功能和硬件要求

9. (可选)如果您的应用功能要求具有正面相机,那么也可以将正面相机功能或硬件要求添加至应用清单文件中,具体取决于您的应用面向何种版本的 Windows Phone:

● ID_HW_FRONTCAMERA:Windows Phone OS 7.1 应用可以使用此功能。通过该功能,没有正面相机的用户将被告知,您的应用的部分功能无法在其手机上使用,但他们仍可以选择下载它。

● ID_REQ_FRONTCAMERA:Windows Phone 8 应用可以使用该硬件要求。借助此要求,不具有正面相机的用户将无法下载应用。

有关功能和要求的更多信息,请参见 Windows Phone 应用的功能和硬件要求

开始捕捉

Android

1. 开启成功的情况下,在SurfaceHolder.Callback的surfaceChanged函数中调用getParameters函数得到已打开的摄像头的配置参数Parameters对象,如果有需要就修改对象的参数,然后调用setParameters函数设置进去(SDK2.2以后,还可以通过Camera::setDisplayOrientation设置方向);

2. 同样在surfaceChanged函数中,通过Camera::setPreviewDisplay为摄像头设置SurfaceHolder对象,设置成功后调用Camera::startPreview函数开启预览功能,上面3,4两步的代码可以如下所示

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)
{
//已经获得Surface的width和height,设置Camera的参数 
Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewSize(w, h);
ListvSizeList = parameters.getSupportedPictureSizes();
for(int num = 0; num < vSizeList.size(); num++)
{
Size vSize = vSizeList.get(num);
}
if(this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE)
{
//如果是竖屏
parameters.set("orientation", "portrait");
//在2.2以上可以使用
//camera.setDisplayOrientation(90);
}
else
{
parameters.set("orientation", "landscape");
//在2.2以上可以使用
//camera.setDisplayOrientation(0);
}
camera.setParameters(parameters);
try {
//设置显示
camera.setPreviewDisplay(holder);
} catch (IOException exception) {
camera.release();
camera = null;
}
//开始预览
camera.startPreview();
}

3. 假设要支持自动对焦功能,则在需要的情况下,或者在上述surfaceChanged调用完startPreview函数后,可以调用Camera::autoFocus函数来设置自动对焦回调函数,该步是可选操作,有些设备可能不支持,可以通过Camera::getFocusMode函数查询。代码可以参考如下:

// 自动对焦 
camera.autoFocus(new AutoFocusCallback() 
{
@Override 
public void onAutoFocus(boolean success, Camera camera)
{
if (success)
{
// success为true表示对焦成功,改变对焦状态图像
ivFocus.setImageResource(R.drawable.focus2);
}
}
});

4. 在需要拍照的时候,调用takePicture(Camera.ShutterCallback, Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)函数来完成拍照,这个函数中可以四个回调接口,ShutterCallback是快门按下的回调,在这里我们可以设置播放“咔嚓”声之类的操作,后面有三个PictureCallback接口,分别对应三份图像数据,分别是原始图像、缩放和压缩图像和JPG图像,图像数据可以在PictureCallback接口的void onPictureTaken(byte[] data, Camera camera)中获得,三份数据相应的三个回调正好按照参数顺序调用,通常我们只关心JPG图像数据,此时前面两个PictureCallback接口参数可以直接传null;

5. 每次调用takePicture获取图像后,摄像头会停止预览,假如需要继续拍照,则我们需要在上面的PictureCallback的onPictureTaken函数末尾,再次掉哟更Camera::startPreview函数;

6. 在不需要拍照的时候,我们需要主动调用Camera::stopPreview函数停止预览功能,并且调用Camera::release函数释放Camera,以便其他应用程序调用。SDK中建议放在Activity的Pause函数中,但是我觉得放在surfaceDestroyed函数中更好,示例代码如下

// 停止拍照时调用该方法
public void surfaceDestroyed(SurfaceHolder holder)
{
// 释放手机摄像头
camera.release();
}

IOS

下面是imageFromSampleBuffer方法,方法经过一系列转换,将CMSampleBufferRef转为UIImage对象,并返回这个对象:

// Create a UIImage from sample buffer data   
- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer
{
// Get a CMSampleBuffer's Core Video image buffer for the media data
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
// Lock the base address of the pixel buffer
CVPixelBufferLockBaseAddress(imageBuffer, 0);
// Get the number of bytes per row for the pixel buffer
void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
// Get the number of bytes per row for the pixel buffer
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
// Get the pixel buffer width and height
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
// Create a device-dependent RGB color space
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// Create a bitmap graphics context with the sample buffer data
CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
// Create a Quartz image from the pixel data in the bitmap graphics context
CGImageRef quartzImage = CGBitmapContextCreateImage(context);
// Unlock the pixel buffer
CVPixelBufferUnlockBaseAddress(imageBuffer,0);
// Free up the context and color space
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
// Create an image object from the Quartz image
//UIImage *image = [UIImage imageWithCGImage:quartzImage];
UIImage *image = [UIImage imageWithCGImage:quartzImage scale:1.0f orientation:UIImageOrientationRight];
// Release the Quartz image
CGImageRelease(quartzImage);
return (image);
}

还需加入代码

// Create and configure a capture session and start it running
- (void)setupCaptureSession 
{
NSError *error = nil;
// Create the session
AVCaptureSession *session = [[[AVCaptureSession alloc] init] autorelease];
// Configure the session to produce lower resolution video frames, if your 
// processing algorithm can cope. We'll specify medium quality for the
// chosen device.
session.sessionPreset = AVCaptureSessionPresetMedium;
// Find a suitable AVCaptureDevice
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];//这里默认是使用后置摄像头,你可以改成前置摄像头
// Create a device input with the device and add it to the session.
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device 
																	error:&error];
if (!input) {
// Handling the error appropriately.
}
[session addInput:input];
// Create a VideoDataOutput and add it to the session
AVCaptureVideoDataOutput *output = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
[session addOutput:output];
// Configure your output.
dispatch_queue_t queue = dispatch_queue_create("myQueue", NULL);
[output setSampleBufferDelegate:self queue:queue];
dispatch_release(queue);
// Specify the pixel format
output.videoSettings = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey, [NSNumber numberWithInt: 320], (id)kCVPixelBufferWidthKey,  [NSNumber numberWithInt: 240], (id)kCVPixelBufferHeightKey, nil];
AVCaptureVideoPreviewLayer* preLayer = [AVCaptureVideoPreviewLayer layerWithSession: session];
//preLayer = [AVCaptureVideoPreviewLayer layerWithSession:session];
preLayer.frame = CGRectMake(0, 0, 320, 240);
preLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;  
[self.view.layer addSublayer:preLayer];
// If you wish to cap the frame rate to a known value, such as 15 fps, set 
// minFrameDuration.
output.minFrameDuration = CMTimeMake(1, 15);
// Start the session running to start the flow of data
[session startRunning];
// Assign session to an ivar.
//[self setSession:session];
}

Win8

1. 开始视频预览

要执行的最后一项操作是将 CaptureElement.Source 设置为 MediaCapture 对象,并使用StartPreviewAsync 开始预览。

// Start capture preview.
// capturePreview is a CaptureElement defined in XAML.
capturePreview.Source = captureMgr;
await captureMgr.StartPreviewAsync();

2. 旋转摄像头

若要为预览设置视频旋转,请将 Windows.Media.Capture.VideoRotation 枚举值传递到SetPreviewRotation。 VideoRotation 枚举指定旋转视频的量,其中包含 无 (0)、90、180 和 270 度值。

captureMgr.SetPreviewRotation(VideoRotation.Clockwise90Degrees);

若要为录制设置视频旋转,请将 Windows.Media.Capture.VideoRotation 枚举值传递到SetRecordRotation

captureMgr.SetRecordRotation(VideoRotation.Clockwise90Degrees);

3. 获取旋转值

若要获取将用于预览的旋转值,请调用 GetPreviewRotation。此方法返回 VideoRotation 枚举值。

若要获取将用于录制的旋转值,请调用 GetRecordRotation。此方法返回 VideoRotation 枚举值。

以下示例获取预览旋转值。

VideoRotation previewRotation = captureMgr.GetPreviewRotation();

4. 在实时捕获期间添加视频防抖动效果

若要在实时捕获期间添加防抖动效果,请使用 MediaCapture.AddEffectAsync 方法。此方法使用以下参数:

● MediaStreamType - 指定流用于视频录制、视频预览、音频还是照片的MediaStreamType枚举值之一。

● effectActivationID - 实现该效果的可激活运行时类的类标识符。此运行时类必须实现 IMediaExtension接口。 Windows.Media 命名空间提供 VideoEffects类。

● effectSettings - 包含用于该效果的其他配置参数的 IPropertySet。如果不需要为该效果使用任何其他配置,则此参数应该为 null。

你可以多次调用此方法以添加多个效果。

此示例将 VideoStabilization 效果添加到效果链(附加到从设备源出来的源流中)。

MediaCapture captureMgr = new MediaCapture();
await captureMgr.InitializeAsync();
await captureMgr.AddEffectAsync(
    MediaStreamType.VideoRecord,
    Windows.Media.VideoEffects.VideoStabilization,
    null);

调用 ClearEffectsAsync 方法将从流中清除所有效果。

// captureMgr is of type MediaCapture.
await captureMgr.ClearEffectsAsync(MediaStreamType.VideoRecord);

5. 在转码期间添加视频防抖动效果

若要在转码期间添加防抖动效果,请使用 MediaTranscoder.AddVideoEffect 方法并提供实现该效果的可激活运行时类的类标识符。你可以多次调用 AddVideoEffect 以添加多个效果。

此示例将 VideoStabilization 效果添加到 MediaTranscoder 对象。

MediaTranscoder transcoder = new MediaTranscoder();
transcoder.AddVideoEffect("Windows.Media.VideoEffects.VideoStabilization");

调用 MediaTranscoder.ClearEffects 方法将从转码器中清除所有效果。

MediaTranscoder transcoder = new MediaTranscoder();
transcoder.AddVideoEffect("Windows.Media.VideoEffects.VideoStabilization");

WP8

在本节中,创建相机 UI,它包含一个用于显示所拍摄帧的取景器区域和一个用于拍摄图像的快门按钮。

实现基于取景器和相机的事件

若要实现取景器,请将 viewfinderBrush 源设置为 Windows Phone 相机。还实现多个基于相机的事件,如相机初始化、完成拍摄以及图像可用性。

1. 在 MainPage.xaml.cs 中,向 MainPage 类中添加以下代码。

//Code for initialization, capture completed, image availability events; also setting the source for the viewfinder.
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
// Check to see if the camera is available on the phone.
if ((PhotoCamera.IsCameraTypeSupported(CameraType.Primary) == true) ||
(PhotoCamera.IsCameraTypeSupported(CameraType.FrontFacing) == true))
{
// Initialize the camera, when available.
if (PhotoCamera.IsCameraTypeSupported(CameraType.FrontFacing))
{
// Use front-facing camera if available.
cam = new Microsoft.Devices.PhotoCamera(CameraType.FrontFacing);
}
else
{
// Otherwise, use standard camera on back of phone.
cam = new Microsoft.Devices.PhotoCamera(CameraType.Primary);
}
// Event is fired when the PhotoCamera object has been initialized.
cam.Initialized += new EventHandler(cam_Initialized);
// Event is fired when the capture sequence is complete.
cam.CaptureCompleted += new EventHandler(cam_CaptureCompleted);
// Event is fired when the capture sequence is complete and an image is available.
cam.CaptureImageAvailable += new EventHandler(cam_CaptureImageAvailable);
// Event is fired when the capture sequence is complete and a thumbnail image is available.
cam.CaptureThumbnailAvailable += new EventHandler(cam_CaptureThumbnailAvailable);
//Set the VideoBrush source to the camera.
viewfinderBrush.SetSource(cam);
}
else
{
// The camera is not supported on the phone.
this.Dispatcher.BeginInvoke(delegate()
{
// Write message.
txtDebug.Text = "A Camera is not available on this phone.";
});
// Disable UI.
ShutterButton.IsEnabled = false;
}
}

protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
if (cam != null)
{
// Dispose camera to minimize power consumption and to expedite shutdown.
cam.Dispose();
// Release memory, ensure garbage collection.
cam.Initialized -= cam_Initialized;
cam.CaptureCompleted -= cam_CaptureCompleted;
cam.CaptureImageAvailable -= cam_CaptureImageAvailable;
cam.CaptureThumbnailAvailable -= cam_CaptureThumbnailAvailable;
}
}

该代码使用 OnNavigatedTo(NavigationEventArgs)方法创建名为 cam 的 PhotoCamera 对象并配置多个事件。该代码还将 VideoBrush 源设置为手机相机对象 cam。

如果除了主相机之外,手机还有一个正面相机,则应用将使用此正面相机。并非所有手机都有正面相机,因此 IsCameraTypeSupported(CameraType) 方法还用于在创建 PhotoCamera 对象之前检查该对象是否可用。还有可能手机没有任何类型的相机。这种情况下,会禁用 UI 并且显示一条消息。

为了说明 Windows Phone 执行模型,在 OnNavigatedTo(NavigationEventArgs) 方法中初始化 PhotoCamera 对象并在 OnNavigatingFrom(NavigatingCancelEventArgs) 方法期间进行显式释放。在 OnNavigatedTo(NavigationEventArgs) 中添加 PhotoCamera 事件的事件处理程序并在 OnNavigatingFrom(NavigatingCancelEventArgs) 中删除以帮助释放内存。

说明:有关如何从相机预览缓冲区分析和处理单个帧的信息,请参见如何在 Windows Phone 的相机应用中使用灰度

2. 在 MainPage.xaml.cs 中,向 MainPage 类中添加以下代码。

// Update the UI if initialization succeeds.
void cam_Initialized(object sender, Microsoft.Devices.CameraOperationCompletedEventArgs e)
{
if (e.Succeeded)
{
this.Dispatcher.BeginInvoke(delegate()
{
// Write message.
txtDebug.Text = "Camera initialized.";
});
}
}

该代码使用相机 Initialized 事件更新名为 txtDebug 的 TextBlock。需要使用 BeginInvoke 方法更新状态,因为应用 UI 在另一个线程上运行。

3. 在 MainPage.xaml.cs 中,向 MainPage 类中添加以下代码。

// Ensure that the viewfinder is upright in LandscapeRight.
protected override void OnOrientationChanged(OrientationChangedEventArgs e)
{
if (cam != null)
{
// LandscapeRight rotation when camera is on back of phone.
int landscapeRightRotation = 180;

// Change LandscapeRight rotation for front-facing camera.
if (cam.CameraType == CameraType.FrontFacing) landscapeRightRotation = -180;
// Rotate video brush from camera.
if (e.Orientation == PageOrientation.LandscapeRight)
{
// Rotate for LandscapeRight orientation.
viewfinderBrush.RelativeTransform = new CompositeTransform() { CenterX = 0.5, CenterY = 0.5, Rotation = landscapeRightRotation };
}
else
{
// Rotate for standard landscape orientation.
viewfinderBrush.RelativeTransform =new CompositeTransform() { CenterX = 0.5, CenterY = 0.5, Rotation = 0 };
}
}
base.OnOrientationChanged(e);
}

该代码确保当相机面朝下(LandscapeRight 方向)时取景器 viewfinderBrush 是直立的。如果相机是正面相机,则需要按照与相机位于手机背面时相反的方向旋转相应的画笔。

说明:尽管向任一方向将视频画笔旋转 180 度都会得到相同的结果,但该代码提供了 UI 方向如何基于相机类型的示例。

4. 在 MainPage.xaml.cs 中,向 MainPage 类中添加以下代码。

private void ShutterButton_Click(object sender, RoutedEventArgs e)
{
if (cam != null)
{
try
{
// Start image capture.
cam.CaptureImage();
}
catch (Exception ex)
{
this.Dispatcher.BeginInvoke(delegate()
{
// Cannot capture an image until the previous capture has completed.
txtDebug.Text = ex.Message;
});
}
}
}
void cam_CaptureCompleted(object sender, CameraOperationCompletedEventArgs e)
{
// Increments the savedCounter variable used for generating JPEG file names.
savedCounter++;
}

该代码实现快门按钮和完成拍摄事件。快门按钮是使用 XAML 代码创建的软件按钮,用于拍摄静止图像。在此项目中使用 CaptureCompleted 事件来递增 savedCounter 变量。在下一节中它用于 JPEG 命名。

保存到媒体库和本地文件夹

照相时拍摄两个图像。一个是高分辨率图像,另一个是用于在库视图中提供高分辨率照片的缩略图图像。本节演示如何将高分辨率图像放置到手机媒体库中。还演示如何将高分辨率图像和缩略图图像保存到本地文件夹。

将图像保存到媒体库和本地文件夹的步骤:

1. 在 MainPage.xaml.cs 中,向 MainPage 类中添加以下代码。

// Informs when full resolution photo has been taken, saves to local media library and the local folder.
void cam_CaptureImageAvailable(object sender, Microsoft.Devices.ContentReadyEventArgs e)
{
string fileName = savedCounter + ".jpg";
try
{
// Write message to the UI thread.
Deployment.Current.Dispatcher.BeginInvoke(delegate()
{
txtDebug.Text = "Captured image available, saving photo.";
});
// Save photo to the media library camera roll.
library.SavePictureToCameraRoll(fileName, e.ImageStream);
// Write message to the UI thread.
Deployment.Current.Dispatcher.BeginInvoke(delegate()
{
txtDebug.Text = "Photo has been saved to camera roll.";
});
// Set the position of the stream back to start
e.ImageStream.Seek(0, SeekOrigin.Begin);
// Save photo as JPEG to the local folder.
using (IsolatedStorageFile isStore = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream targetStream = isStore.OpenFile(fileName, FileMode.Create, FileAccess.Write))
{
// Initialize the buffer for 4KB disk pages.
byte[] readBuffer = new byte[4096];
int bytesRead = -1;
// Copy the image to the local folder. 
while ((bytesRead = e.ImageStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
{
targetStream.Write(readBuffer, 0, bytesRead);
}
}
}
// Write message to the UI thread.
Deployment.Current.Dispatcher.BeginInvoke(delegate()
{
txtDebug.Text = "Photo has been saved to the local folder.";
});
}
finally
{
// Close image stream
e.ImageStream.Close();
}
}

// Informs when thumbnail photo has been taken, saves to the local folder
// User will select this image in the Photos Hub to bring up the full-resolution. 
public void cam_CaptureThumbnailAvailable(object sender, ContentReadyEventArgs e)
{
string fileName = savedCounter + "_th.jpg";

try
{
// Write message to UI thread.
Deployment.Current.Dispatcher.BeginInvoke(delegate()
{
txtDebug.Text = "Captured image available, saving thumbnail.";
});
// Save thumbnail as JPEG to the local folder.
using (IsolatedStorageFile isStore = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream targetStream = isStore.OpenFile(fileName, FileMode.Create, FileAccess.Write))
{
// Initialize the buffer for 4KB disk pages.
byte[] readBuffer = new byte[4096];
int bytesRead = -1;
// Copy the thumbnail to the local folder. 
while ((bytesRead = e.ImageStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
{
targetStream.Write(readBuffer, 0, bytesRead);
}
}
}
// Write message to UI thread.
Deployment.Current.Dispatcher.BeginInvoke(delegate()
{
txtDebug.Text = "Thumbnail has been saved to the local folder.";
});
}
finally
{
// Close image stream
e.ImageStream.Close();
}
}

该代码实现 CaptureImageAvailable 和 CaptureThumbnailAvailable 事件。第一个方法将高分辨率图像保存到媒体库和本地文件夹。第二个方法演示如何将缩略图图像保存到本地文件夹。

说明:若要将照片保存到照片中心的“保存的图片”相册中,请使用 SavePicture() 方法。此示例中使用的 SavePictureToCameraRoll() 方法将图像保存到“本机拍照”相册中。

2. 在手机上,通过选择“调试 | 启动调试”菜单命令来运行应用。通过按 SH 按钮测试应用。txtDebug TextBlock 将指示保存到本地文件夹的保存操作的状态。退出应用之后,您可以在照片中心的“本机拍照”文件夹中找到使用该应用拍摄的照片。

以下示例演示此时如何显示 UI。

AP_Con_CameraWireFrame

在此示例中,软件快门按钮 SH 显示在 UI 的右上角。Mango 的照片出现在名为 viewfinderCanvas 的 Canvas 控件中。

3. 既然您已完成了这个基本相机应用,那么您可以使用它来完成以下主题:

● 如何在 Windows Phone 的应用中使用相机闪光灯

● 如何在 Windows Phone 的应用中使用相机对焦

● 如何在 Windows Phone 应用中调整所捕获照片的分辨率

● 如何访问 Windows Phone 中的硬件相机快门按钮

举报
顶部