使用方向感測器

學習如何使用方向感測器來判斷裝置的方向。

這個範例建立了一個簡單的應用程式,依賴方向感測器作為輸入裝置。 方向感測器是多種環境感測器之一,讓應用程式能對裝置方向的變化做出反應。

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 方法,對當前方向進行一次性讀取。