學習如何使用方向感測器來判斷裝置的方向。
這個範例建立了一個簡單的應用程式,依賴方向感測器作為輸入裝置。 方向感測器是多種環境感測器之一,讓應用程式能對裝置方向的變化做出反應。
Note
本文重點介紹示範如何使用方向感測器的程式碼。 關於方向感測器的概述,請參見 感測器:方向感測器。
Prerequisites
你應該熟悉方向感測器及其用途。 參見 感測器:方向感測器。
你使用的裝置必須支援方向感測器。
方向感測器類型
Windows.Devices.Sensors 命名空間中包含兩種不同類型的方向感應器 API:OrientationSensor 和 SimpleOrientation。 雖然這兩種感測器都是方向感測器,但這個詞過於過載,且它們用於非常不同的用途。 不過,由於兩者都是方向感測器,本文會涵蓋它們。
OrientationSensor API 用於三維應用程式,兩者分別取得四元數矩陣與旋轉矩陣。 四元數最容易理解為點 [x,y,z] 繞任意軸旋轉(與旋轉矩陣相對,旋轉矩陣代表繞三個軸的旋轉)。 四元數背後的數學相當特殊,因為它涉及複數的幾何性質和虛數的數學性質,但操作起來很簡單,像 DirectX 這類框架也支援它們。 複雜的 3D 應用程式可以利用方位感測器調整使用者的視角。 此感測器結合了加速度計、陀螺儀與羅盤的輸入。
SimpleOrientationSensor API 用於判斷裝置目前的實體方向,例如直向朝上、直向朝下、橫向朝左和橫向朝右等定義。 它也能偵測裝置是正面朝上還是正面朝下。 這個感測器不會回傳像「直立向上」或「橫向左」這類屬性,而是回傳旋轉值,例如:「未旋轉」、「逆時針旋轉 90 度」等等。 下表將常見的方向屬性映射到對應的感測器讀數。
| 導覽 | 對應的感測器讀數 |
|---|---|
| 畫像向上 | 未旋轉 |
| 左翼景觀 | 逆時針旋轉90度 |
| 肖像倒下 | 逆時針旋轉180度 |
| 景觀權 | 逆時針旋轉270度 |
範例代碼 - 方向感測器
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml.Controls;
using Windows.Devices.Sensors;
namespace DevicesDemo.Pages
{
public sealed partial class OrientationSensorPage : Page
{
private OrientationSensor? orientationSensor;
public OrientationSensorPage()
{
InitializeComponent();
// Get the default orientation sensor object.
orientationSensor = OrientationSensor.GetDefault();
if (orientationSensor != null)
{
// Establish the report interval.
uint minReportInterval = orientationSensor.MinimumReportInterval;
uint reportInterval = minReportInterval > 16 ? minReportInterval : 16;
orientationSensor.ReportInterval = reportInterval;
// Assign an event handler for the reading-changed event.
orientationSensor.ReadingChanged += OrientationSensor_ReadingChanged;
}
else
{
statusBar.Message = "No orientation sensor was found.";
statusBar.Severity = InfoBarSeverity.Error;
statusBar.IsOpen = true;
}
}
// This event handler writes the current orientation
// reading to the text blocks on the XAML page.
private void OrientationSensor_ReadingChanged(OrientationSensor sender, OrientationSensorReadingChangedEventArgs args)
{
DispatcherQueue?.TryEnqueue(DispatcherQueuePriority.Normal, () =>
{
OrientationSensorReading reading = args.Reading;
// Quaternion values
txtQuaternionX.Text = String.Format("{0,8:0.00000}", reading.Quaternion.X);
txtQuaternionY.Text = String.Format("{0,8:0.00000}", reading.Quaternion.Y);
txtQuaternionZ.Text = String.Format("{0,8:0.00000}", reading.Quaternion.Z);
txtQuaternionW.Text = String.Format("{0,8:0.00000}", reading.Quaternion.W);
// Rotation Matrix values
txtM11.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M11);
txtM12.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M12);
txtM13.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M13);
txtM21.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M21);
txtM22.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M22);
txtM23.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M23);
txtM31.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M31);
txtM32.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M32);
txtM33.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M33);
});
}
}
}
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Margin="24">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" MinWidth="66"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" MinWidth="66"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" MinWidth="66"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="44"/>
<RowDefinition Height="44"/>
<RowDefinition Height="44"/>
</Grid.RowDefinitions>
<TextBlock Text="M11:" Style="{StaticResource LabelTextBlockStyle}"/>
<TextBlock x:Name="txtM11" Grid.Column="1" Text="---"/>
<TextBlock Text="M12:" Grid.Row="1" Style="{StaticResource LabelTextBlockStyle}"/>
<TextBlock x:Name="txtM12" Grid.Column="1" Grid.Row="1" Text="---"/>
<TextBlock Text="M13:" Grid.Row="2" Style="{StaticResource LabelTextBlockStyle}"/>
<TextBlock x:Name="txtM13" Grid.Column="1" Grid.Row="2" Text="---"/>
<TextBlock Text="M21:" Grid.Column="2" Grid.Row="0" Style="{StaticResource LabelTextBlockStyle}"/>
<TextBlock x:Name="txtM21" Grid.Column="3" Grid.Row="0" Text="---"/>
<TextBlock Text="M22:" Grid.Column="2" Grid.Row="1" Style="{StaticResource LabelTextBlockStyle}"/>
<TextBlock x:Name="txtM22" Grid.Column="3" Grid.Row="1" Text="---"/>
<TextBlock Text="M23:" Grid.Column="2" Grid.Row="2" Style="{StaticResource LabelTextBlockStyle}"/>
<TextBlock x:Name="txtM23" Grid.Column="3" Grid.Row="2" Text="---"/>
<TextBlock Text="M31:" Grid.Column="4" Grid.Row="0" Style="{StaticResource LabelTextBlockStyle}"/>
<TextBlock x:Name="txtM31" Grid.Column="5" Grid.Row="0" Text="---"/>
<TextBlock Text="M32:" Grid.Column="4" Grid.Row="1" Style="{StaticResource LabelTextBlockStyle}"/>
<TextBlock x:Name="txtM32" Grid.Column="5" Grid.Row="1" Text="---"/>
<TextBlock Text="M33:" Grid.Column="4" Grid.Row="2" Style="{StaticResource LabelTextBlockStyle}"/>
<TextBlock x:Name="txtM33" Grid.Column="5" Grid.Row="2" Text="---"/>
</Grid>
<Grid Margin="24" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="44"/>
<RowDefinition Height="44"/>
<RowDefinition Height="44"/>
<RowDefinition Height="44"/>
</Grid.RowDefinitions>
<TextBlock Text="Quaternion X:" Style="{StaticResource LabelTextBlockStyle}"/>
<TextBlock x:Name="txtQuaternionX" Grid.Column="1" Grid.Row="0" Text="---"/>
<TextBlock Text="Quaternion Y:" Grid.Row="1" Style="{StaticResource LabelTextBlockStyle}"/>
<TextBlock x:Name="txtQuaternionY" Grid.Column="1" Grid.Row="1" Text="---"/>
<TextBlock Text="Quaternion Z:" Grid.Row="2" Style="{StaticResource LabelTextBlockStyle}"/>
<TextBlock x:Name="txtQuaternionZ" Grid.Column="1" Grid.Row="2" Text="---"/>
<TextBlock Text="Quaternion W:" Grid.Row="3" Style="{StaticResource LabelTextBlockStyle}"/>
<TextBlock x:Name="txtQuaternionW" Grid.Column="1" Grid.Row="3" Text="---"/>
</Grid>
<InfoBar x:Name="statusBar" Grid.Row="2"/>
</Grid>
應用程式執行時,你可以透過移動裝置來改變方向值。
前一個範例展示了你需要撰寫的必要程式碼,以便將方向感測器輸入整合進你的應用程式中。
連接到感測器
呼叫 GetDefault 方法與預設方向感測器建立連線。
private OrientationSensor? orientationSensor;
// ...
orientationSensor = OrientationSensor.GetDefault();
你也可以呼叫 FromIdAsync,從 DeviceInformation.Id 值建立一個OrientationSensor物件。 欲了解更多資訊,請參閱 列舉裝置。
若未偵測到方向感測器,狀態訊息會更新以通知使用者。
設定報告間隔
報告間隔由頁面的建構子設定。 此程式碼會取得裝置所支援的最小間隔,並與請求的 16 毫秒間隔(約 60 Hz 更新率)比較。 若最小支援區間大於請求區間,程式碼會將該值設為最小值。 否則,它會將值設定為所要求的區間。
uint minReportInterval = orientationSensor.MinimumReportInterval;
uint reportInterval = minReportInterval > 16 ? minReportInterval : 16;
orientationSensor.ReportInterval = reportInterval;
讀取感測器資料
新的方向感測器資料會被擷取在 ReadingChanged 事件處理程式中。 每當感測器驅動程式從感測器接收到新資料時,就會利用這個事件將數值傳給你的應用程式。 在此範例中,這些新值會寫入對應頁面的 XAML 文字區塊。
orientationSensor.ReadingChanged += OrientationSensor_ReadingChanged;
// ...
private void OrientationSensor_ReadingChanged(OrientationSensor sender, OrientationSensorReadingChangedEventArgs args)
{
DispatcherQueue?.TryEnqueue(DispatcherQueuePriority.Normal, () =>
{
OrientationSensorReading reading = args.Reading;
// Quaternion values
txtQuaternionX.Text = String.Format("{0,8:0.00000}", reading.Quaternion.X);
txtQuaternionY.Text = String.Format("{0,8:0.00000}", reading.Quaternion.Y);
txtQuaternionZ.Text = String.Format("{0,8:0.00000}", reading.Quaternion.Z);
txtQuaternionW.Text = String.Format("{0,8:0.00000}", reading.Quaternion.W);
// Rotation Matrix values
txtM11.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M11);
txtM12.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M12);
txtM13.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M13);
txtM21.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M21);
txtM22.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M22);
txtM23.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M23);
txtM31.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M31);
txtM32.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M32);
txtM33.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M33);
});
}
範例程式碼 - 簡單方向感測器
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml.Controls;
using Windows.Devices.Sensors;
namespace DevicesDemo.Pages
{
public sealed partial class SimpleOrientationPage : Page
{
private SimpleOrientationSensor? simpleOrientationSensor;
public SimpleOrientationPage()
{
InitializeComponent();
// Get the default simple orientation sensor object.
simpleOrientationSensor = SimpleOrientationSensor.GetDefault();
// Assign an event handler.
if (simpleOrientationSensor != null)
{
// Assign an event handler for the reading-changed event.
simpleOrientationSensor.OrientationChanged
+= SimpleOrientationSensor_OrientationChanged;
}
else
{
statusBar.Message = "No simple orientation sensor was found.";
statusBar.Severity = InfoBarSeverity.Error;
statusBar.IsOpen = true;
}
}
// This event handler writes the current simple orientation
// reading to the text block on the XAML page.
private void SimpleOrientationSensor_OrientationChanged(SimpleOrientationSensor sender,
SimpleOrientationSensorOrientationChangedEventArgs args)
{
DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Normal, () =>
{
switch (args.Orientation)
{
case SimpleOrientation.NotRotated:
txtOrientation.Text = "Not Rotated";
break;
case SimpleOrientation.Rotated90DegreesCounterclockwise:
txtOrientation.Text = "Rotated 90 Degrees Counterclockwise";
break;
case SimpleOrientation.Rotated180DegreesCounterclockwise:
txtOrientation.Text = "Rotated 180 Degrees Counterclockwise";
break;
case SimpleOrientation.Rotated270DegreesCounterclockwise:
txtOrientation.Text = "Rotated 270 Degrees Counterclockwise";
break;
case SimpleOrientation.Faceup:
txtOrientation.Text = "Faceup";
break;
case SimpleOrientation.Facedown:
txtOrientation.Text = "Facedown";
break;
default:
txtOrientation.Text = "Unknown orientation";
break;
}
});
}
}
}
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Margin="24">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="44"/>
</Grid.RowDefinitions>
<TextBlock Text="Orientation:" Style="{StaticResource LabelTextBlockStyle}"/>
<TextBlock x:Name="txtOrientation" Grid.Column="1" Text="---"/>
</Grid>
<InfoBar x:Name="statusBar" Grid.Row="1"/>
</Grid>
應用程式執行時,你可以透過移動裝置來改變方向值。
前一個範例展示了你需要撰寫的必要程式碼,以便將簡單方向感測器輸入整合進你的應用程式。
連接到簡單的方向感測器
呼叫 GetDefault 方法與預設方向感測器建立連線。
private SimpleOrientationSensor? simpleOrientationSensor;
// ...
simpleOrientationSensor = SimpleOrientationSensor.GetDefault();
你也可以呼叫 FromIdAsync,從 DeviceInformation.Id 值建立一個SimpleOrientationSensor物件。 欲了解更多資訊,請參閱 列舉裝置。
若未偵測到簡單的方向感測器,狀態訊息會更新以通知使用者。
讀取簡單的方向感測器資料
新的簡單方向感測器資料會被擷取到 OrientationChanged 事件處理器中。 每當感測器驅動程式從感測器接收到新資料時,就會利用這個事件將數值傳給你的應用程式。 在這個例子中,這些新值會寫入 XAML 中對應頁面的文字區塊。
simpleOrientationSensor.OrientationChanged
+= SimpleOrientationSensor_OrientationChanged;
// ...
private void SimpleOrientationSensor_OrientationChanged(SimpleOrientationSensor sender,
SimpleOrientationSensorOrientationChangedEventArgs args)
{
DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Normal, () =>
{
switch (args.Orientation)
{
case SimpleOrientation.NotRotated:
txtOrientation.Text = "Not Rotated";
break;
case SimpleOrientation.Rotated90DegreesCounterclockwise:
txtOrientation.Text = "Rotated 90 Degrees Counterclockwise";
break;
case SimpleOrientation.Rotated180DegreesCounterclockwise:
txtOrientation.Text = "Rotated 180 Degrees Counterclockwise";
break;
case SimpleOrientation.Rotated270DegreesCounterclockwise:
txtOrientation.Text = "Rotated 270 Degrees Counterclockwise";
break;
case SimpleOrientation.Faceup:
txtOrientation.Text = "Faceup";
break;
case SimpleOrientation.Facedown:
txtOrientation.Text = "Facedown";
break;
default:
txtOrientation.Text = "Unknown orientation";
break;
}
});
}
作為事件的 OrientationChanged 替代方案,你可以呼叫 GetCurrentOrientation 方法,對當前方向進行一次性讀取。