[Из песочницы] Анимированное изменение ориентации экрана в приложении Windows Phone

Легко заметить что в Магазине Windows Phone очень много приложений вообще работающих только в портретной ориентации. Отчасти это объясняется тем, что таково положение вещей по умолчанию в Windows Phone. Образцом же приложения, по максимуму использующему возможность опрокидывания экрана, можно считать стандартный Калькулятор.

4e8eb8396d234ef4ae1a5d4c191bc6e6.png3e5200e5593b4ac78a5e866b38be7d02.png

В портретной ориентации мы получаем простой калькулятор. А в альбомной уже — инженерный.
3776b3a5fabe4ae68cd141c4cc64cf84.png

Но самое прекрасное — это анимация перехода из одного режима в другой.

Разрабатывая свой небольшой проект: приложение TapHint, реализовал несколько вещей, улучшающих восприятие интерфейса программы. Почему-то мне показалась нетривиальной реализация такой полезной вещи как анимация переворота телефона. И в интернете как-то не без усилий была найдена информация по этой теме. Само приложение работает со стандартными NFC-метками NDEF формата, записывая в них информацию и, соответственно, читая её из них.

Для страницы приложения, показывающей записанную в метку информацию, и решено было применить поддержку анимированных переворачиваний. Результат выглядит так.

Портретный и альбомный вид:

c1bd56d4f0bd493bb065180d9b21271e.png

3c53d39beed346cf83b82244fd6093a6.png

Кадры из анимации промежуточных состояний:

dec66bb859864ed69d90e6be6adc5078.png

b6e2675356c149738f0163d7f7b086dc.png

Как оказалось, реализация обработок всех возможных видов опрокидывания телефона получается довольно громоздкой.

XAML страницы ShowPage.xaml


    
        
    

    
        
            
                
                    
                
            
        
    

    
    

        
        
            
                 
                    
                        
                            
                        
                        
                            
                                
                            
                        
                        
                            
                                
                            
                        
                    
                
                
                    
                        
                            
                        
                        
                            
                                
                            
                        
                        
                            
                                
                            
                        
                    
                
                
                    
                        
                            
                                
                            
                        
                        
                            
                                
                            
                        
                    
                
                
                    
                        
                            
                                
                            
                        
                        
                            
                                
                            
                        
                    
                
                
                    
                        
                            
                        
                        
                            
                                
                            
                        
                    
                
                
                    
                        
                            
                        
                        
                            
                                
                            
                        
                    
                
            
        

        
            
            
        

        
        
            
            
            
        

        
        
            
                
                    
                
                
                    
                
            
        
    




Для начала нужно указать с помощью параметра SupportedOrientations=«PortraitOrLandscape», что поддерживаем обе ориентации страницы приложения. Параметр Orientation=«Portrait» задаёт ориентацию по умолчанию. Но это будет лишь опрокидывать страницу туда-сюда без всяких анимаций. Для описания набора анимаций нужно задействовать VisualStateManager. В нём можно описать все переходы-VisualState’ы, которыми хотим воспользоваться в различных ситуациях. В каждом VisualState можно описать Storyboard с анимациями (если их несколько, то они проигрываются одновременно).

Например, VisualState для перехода из портретной ориентации в альбомную правую можно назвать x: Name=»FromPToLR». Можно пояснить что «альбомная правая» — это альбомная при положении правой грани телефона внизу. Таким образом получаем одну портретную ориентацию и две альбомные. Ситуация отягчается тем, что необходимо описать переходы от одной ориентации к другой в обоих направлениях. И что может быть неочевидным — переход из одного альбомного положения в другое и обратно. Итого шесть переходов.

CS страницы ShowPage.xaml.cs
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, можно применить нужную анимацию.

Остаётся надеяться что будет больше приложений для телефонов, в которых не будут лениться делать эти полезные украшательства.

© Habrahabr.ru