| Невозможно пройти тесты, в окне с вопросами пусто |
Акселерометр, микрофон, панель приложения
Цель работы: Научиться работать с акселерометром, микрофоном, панелью приложения в Silverlight-приложениях
34.1. Акселерометр
В этом примере мы хотели бы использовать данные, получаемые с акселерометра, для управления экранным объектом. Объект должен перемещаться в том направлении, в котором наклонён телефон, если телефон лежит экраном вверх на горизонтальной поверхности, объект должен быть неподвижным.
Создадим новый проект, P27_1, он будет состоять из одной страницы, оформление которой, в целом, аналогично тому, которое было представлено в проекте P26_1. На рис. 34.1 вы можете видеть экран приложения, в листинге 34.1 – код страницы.
<phone:PhoneApplicationPage
x:Class="P27_1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="480"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Landscape" Orientation="LandscapeLeft"
shell:SystemTray.IsVisible="False">
<!--LayoutRoot представляет корневую сетку, где размещается все содержимое страницы-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--ContentPanel — поместите здесь дополнительное содержимое-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="0,0,0,0">
<Image Height="480" HorizontalAlignment="Left" Name="image1" Stretch="Fill"
VerticalAlignment="Top" Width="800" Source="/P27_1;component/Res/Background.jpg" />
<Canvas>
<Ellipse Height="100"
Name="ellipse1"
Stroke="Black"
StrokeThickness="1"
Width="100"
Fill="#FF00CB00"
Canvas.Top="190"
Canvas.Left="350"/>
</Canvas>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
Листинг
34.1.
Код страницы MainPage
Обратите внимание на то, что здесь мы размещаем элемент Ellipse внутри элемента Canvas (холст). В результате, у элемента Ellipse появляются присоединенные свойства (Canvas.Top и Canvas.Left). Они задают позиционирование объекта внутри Canvas, модифицируя их, мы сможем перемещать объект по экрану, ориентируясь на них сможем предотвратить пересечение объектом границ экрана.
Для успешной работы с приведенным примером нам понадобится подключить библиотеки Microsoft.Devices.Sensors и Microsoft.Xna.Framework. Первая нужна для работы с акселерометром. Вторая – для обработки значений типа Vector3, с которыми мы столкнёмся при получении данных от акселерометра.
В листинге 34.2 приведен программный код, обеспечивающий работу нашего примера.
using System;
using System.Windows.Controls;
using Microsoft.Phone.Controls;
using Microsoft.Devices.Sensors;
namespace P27_1
{
public partial class MainPage : PhoneApplicationPage
{
//Для акселерометра
Accelerometer accelerometer;
// Конструктор
public MainPage()
{
InitializeComponent();
//Новый экземпляр объекта Accelerometer
accelerometer = new Accelerometer();
//Время между обновлениями показаний акселерометра
accelerometer.TimeBetweenUpdates = TimeSpan.FromMilliseconds(20);
//Подключаем обработчик события, возникающего при получении новых данных
accelerometer.CurrentValueChanged += new EventHandler<SensorReadingEventArgs
<AccelerometerReading>>(accelerometer_CurrentValueChanged);
//Запуск акселерометра
accelerometer.Start();
}
//Обработчик
void accelerometer_CurrentValueChanged(object sender,
SensorReadingEventArgs<AccelerometerReading> e)
{
//Вызов метода из потока пользовательского интерфейса
Dispatcher.BeginInvoke(() => MoveObject(e.SensorReading));
}
//Метод для перемещения объекта
void MoveObject(AccelerometerReading accData)
{
//Переместим объект по оси Y
Canvas.SetLeft(ellipse1, Canvas.GetLeft(ellipse1) - accData.Acceleration.Y*5);
//Переместим объект по оси X
Canvas.SetTop(ellipse1, Canvas.GetTop(ellipse1) - accData.Acceleration.X*5);
//Проверим на столкновение с границами экрана
CheckBounds();
}
void CheckBounds()
{
//При выходе объекта за границы экрана
//Установим позицию так, чтобы он касался
//границ экрана
if (Canvas.GetLeft(ellipse1) < 0 )
{
Canvas.SetLeft(ellipse1, 0);
}
if (Canvas.GetLeft(ellipse1) > 700)
{
Canvas.SetLeft(ellipse1, 700);
}
if (Canvas.GetTop(ellipse1) < 0)
{
Canvas.SetTop(ellipse1, 0);
}
if (Canvas.GetTop(ellipse1) > 380)
{
Canvas.SetTop(ellipse1, 380);
}
}
}
}
Листинг
34.2.
Программный код страницы
В конструкторе мы инициализируем объект типа Accelerometer, задаём интервал обновления данных, назначаем обработчик события, возникающего при получении новых данных и запускаем акселерометр.
Обратите внимание на обработчик accelerometer_CurrentValueChanged. Этот обработчик выполняется в собственном потоке, а пользовательский интерфейс – в собственном, так называемом UI-потоке. В итоге, для того, чтобы обратиться к элементу пользовательского интерфейса, нам понадобится воспользоваться диспетчером (Dispatcher), с помощью которого можно направить задание из одного потока в другой. В данном случае мы используем лямбда-выражение.
Перемещая объект, мы модифицируем его свойства Canvas.Top и Canvas.Left. Для работы с ними существуют соответствующие методы класса Canvas, например Canvas.GetTop(object) получает значение Top для объекта, SetTop – устанавливает.
После перемещения мы проверяем, не пересек ли объект границы экрана (мы работаем в ландшафтной ориентации), если пересек – возвращаем его к границам экрана.
