Невозможно пройти тесты, в окне с вопросами пусто |
Акселерометр, микрофон, панель приложения
Цель работы: Научиться работать с акселерометром, микрофоном, панелью приложения в 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 – устанавливает.
После перемещения мы проверяем, не пересек ли объект границы экрана (мы работаем в ландшафтной ориентации), если пересек – возвращаем его к границам экрана.