[Из песочницы] Анимированное изменение ориентации экрана в приложении Windows Phone
Легко заметить что в Магазине Windows Phone очень много приложений вообще работающих только в портретной ориентации. Отчасти это объясняется тем, что таково положение вещей по умолчанию в Windows Phone. Образцом же приложения, по максимуму использующему возможность опрокидывания экрана, можно считать стандартный Калькулятор.
В портретной ориентации мы получаем простой калькулятор. А в альбомной уже — инженерный.
Но самое прекрасное — это анимация перехода из одного режима в другой.
Разрабатывая свой небольшой проект: приложение TapHint, реализовал несколько вещей, улучшающих восприятие интерфейса программы. Почему-то мне показалась нетривиальной реализация такой полезной вещи как анимация переворота телефона. И в интернете как-то не без усилий была найдена информация по этой теме. Само приложение работает со стандартными NFC-метками NDEF формата, записывая в них информацию и, соответственно, читая её из них.
Для страницы приложения, показывающей записанную в метку информацию, и решено было применить поддержку анимированных переворачиваний. Результат выглядит так.
Портретный и альбомный вид:
Кадры из анимации промежуточных состояний:
Как оказалось, реализация обработок всех возможных видов опрокидывания телефона получается довольно громоздкой.
Для начала нужно указать с помощью параметра SupportedOrientations=«PortraitOrLandscape», что поддерживаем обе ориентации страницы приложения. Параметр Orientation=«Portrait» задаёт ориентацию по умолчанию. Но это будет лишь опрокидывать страницу туда-сюда без всяких анимаций. Для описания набора анимаций нужно задействовать VisualStateManager. В нём можно описать все переходы-VisualState’ы, которыми хотим воспользоваться в различных ситуациях. В каждом VisualState можно описать Storyboard с анимациями (если их несколько, то они проигрываются одновременно).
Например, VisualState для перехода из портретной ориентации в альбомную правую можно назвать x: Name=»FromPToLR». Можно пояснить что «альбомная правая» — это альбомная при положении правой грани телефона внизу. Таким образом получаем одну портретную ориентацию и две альбомные. Ситуация отягчается тем, что необходимо описать переходы от одной ориентации к другой в обоих направлениях. И что может быть неочевидным — переход из одного альбомного положения в другое и обратно. Итого шесть переходов.
using System.Linq;
using System.Windows;
using System.Windows.Navigation;
using System.Windows.Media;
using Microsoft.Phone.Controls;
namespace Tap_Hint
{
public partial class ShowPage : PhoneApplicationPage
{
private PageOrientation m_ePageOrientation = PageOrientation.PortraitUp;
public ShowPage()
{
InitializeComponent();
bool isDefaultColor = SettingsStore.get().extractParamBool(SettingsStore.StoringParams.USE_DEFAULT_COLOR.ToString());
showTextBlock.FontSize = SettingsStore.g_FontSizeStep * (SettingsStore.get().extractParamInt(SettingsStore.StoringParams.FONT_SIZE.ToString()) + 1);
if (isDefaultColor)
{
showTextBlock.Foreground = (Brush)Application.Current.Resources["PhoneAccentBrush"];
}
else
{
showTextBlock.Foreground = new SolidColorBrush(Colors.White);
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (e.NavigationMode == NavigationMode.New)
{
showPageStoryboardTo.Begin();
if (NavigationContext.QueryString.Values != null && NavigationContext.QueryString.Values.ToArray() != null &&
NavigationContext.QueryString.Values.ToArray().Length > 0)
{
if ("tagParam".Equals(NavigationContext.QueryString.Keys.ElementAt(0)))
{
showTextBlock.Text = NavigationContext.QueryString.Values.ElementAt(0);
}
if ("isEnc".Equals(NavigationContext.QueryString.Keys.ElementAt(1)) && "true".Equals(NavigationContext.QueryString.Values.ElementAt(1)))
{
showBorder.BorderBrush = new SolidColorBrush(Color.FromArgb(255, 250, 250, 0));
}
else if ("isEnc".Equals(NavigationContext.QueryString.Keys.ElementAt(1)) && "false".Equals(NavigationContext.QueryString.Values.ElementAt(1)))
{
showBorder.BorderBrush = new SolidColorBrush(Color.FromArgb(255, 20, 230, 30));
}
else if ("isEnc".Equals(NavigationContext.QueryString.Keys.ElementAt(1)) && "spec".Equals(NavigationContext.QueryString.Values.ElementAt(1)))
{//spec - error or encoded on enother device
showBorder.BorderBrush = new SolidColorBrush(Color.FromArgb(255, 240, 30, 30));
}
}
else
{
showTextBlock.Text = "no param";
}
}
}
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
if (e.NavigationMode != NavigationMode.Back)
{//leave the page due to long back button or Windows button, stay on the page
return;
}
App.Current.Terminate(); //application exiting
}
private void showPage_OrientationChanged(object sender, OrientationChangedEventArgs e)
{
//playback animations on orientation change
if (m_ePageOrientation == PageOrientation.PortraitUp && e.Orientation == PageOrientation.LandscapeRight)
{
VisualStateManager.GoToState(this, "FromPToLR", true);
}
else if (m_ePageOrientation == PageOrientation.PortraitUp && e.Orientation == PageOrientation.LandscapeLeft)
{
VisualStateManager.GoToState(this, "FromPToLL", true);
}
else if (m_ePageOrientation == PageOrientation.LandscapeRight && e.Orientation == PageOrientation.PortraitUp)
{
VisualStateManager.GoToState(this, "FromLRToP", true);
}
else if (m_ePageOrientation == PageOrientation.LandscapeLeft && e.Orientation == PageOrientation.PortraitUp)
{
VisualStateManager.GoToState(this, "FromLLToP", true);
}
else if (m_ePageOrientation == PageOrientation.LandscapeRight && e.Orientation == PageOrientation.LandscapeLeft)
{
VisualStateManager.GoToState(this, "FromLRToLL", true);
}
else if (m_ePageOrientation == PageOrientation.LandscapeLeft && e.Orientation == PageOrientation.LandscapeRight)
{
VisualStateManager.GoToState(this, "FromLLToLR", true);
}
//saving current orientation mode
if (e.Orientation == PageOrientation.PortraitUp)
{
m_ePageOrientation = PageOrientation.PortraitUp;
}
else if (e.Orientation == PageOrientation.LandscapeRight)
{
VisualStateManager.GoToState(this, "LandscapeState", true);
m_ePageOrientation = PageOrientation.LandscapeRight;
}
else if (e.Orientation == PageOrientation.LandscapeLeft)
{
m_ePageOrientation = PageOrientation.LandscapeLeft;
}
}
}
}
В коде страницы остаётся воспользоваться обработчиком изменения ориентации — showPage_OrientationChanged (). Анализируя то какой была ориентация с помощью значения переменной m_ePageOrientation и то какой ориентация экрана стала в e.Orientation, можно применить нужную анимацию.
Остаётся надеяться что будет больше приложений для телефонов, в которых не будут лениться делать эти полезные украшательства.