Статьи

Создание приложения WP7: поддержка темных и светлых тем


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

Этот пост является частью серии постов, которые я запланировал на основании своих выводов при написании приложения для Windows Phone 7 (см. Предыдущий пост о проблеме кэширования URL-адресов здесь ).

Цвета и темы

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

Когда ваше приложение запускается, текущая активная тема будет влиять на внешний вид вашего приложения. То есть, пока вы явно не определили определенные стили и цвета, все стандартные элементы управления WP7 будут отображаться правильно. Посмотрим, как это работает. Предположим, что ваше чрезвычайно классное и интуитивно понятное приложение имеет красивую блестящую кнопку, такую ​​как эта:

<Button Content="Shiny"/> 

Кнопка будет выглядеть в зависимости от используемой темы (обратите внимание, что цвет акцента темы здесь не имеет значения). Поэтому, если мы запустим наше приложение, мы увидим следующие кнопки:

Когда темная тема (левое изображение) активна, наша кнопка и все остальные стандартные элементы управления автоматически будут иметь черный фон и белую кисть переднего плана. Однако, когда светлая тема активна, WP7 в основном переключает свойства Foreground и Background всех элементов управления, если они явно не определены.

Однако в тот момент, когда вы определяете определенный цвет для элемента управления, WP7 больше не поможет вам с переключением цветов и гарантирует, что ваши элементы управления будут хорошо выглядеть на активном изображении.
Представьте себе необычную желтую кнопку:

<Button Content="Shiny" Background="Yellow"/>

 

Тестирование этой кнопки в белой теме дает приятные результаты (обратите внимание: я не дизайнер, я уже счастлив, если мои цветовые схемы не вызывают головную боль в течение 5 минут):

Теперь посмотрим, как это выглядит в темной теме:

Тьфу .. Мои глаза! Что происходит, так как мы не определили передний план явно (который использует текст и рамку в кнопке), черные границы и черный контент были переключены, а желтый фон — нет. Все-таки, некрасиво или нет, кнопка все еще может использоваться.

Однако предположим, что ваша кнопка действительно должна иметь определенный цвет, например белый.

<Button Content="Shiny" Background="White" Button>

Давайте посмотрим, как это выглядит в обеих темах:

Йиппи, мы сделали белый прямоугольник!

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

Обнаружение темы

Чтобы узнать , какая тема активна Я видел несколько приемов (старая школа стиль, по- видимому, глядя на текущие значения RGB на передней или фоновой кисти) , но на мой взгляд, следующее является наиболее простым (см здесь для а список всех предопределенных ресурсов темы):

bool dark= ((Visibility)Application.Current.Resources["PhoneDarkThemeVisibility"] == Visibility.Visible)

Мы можем просто проверить, является ли PhoneDarkThemeVisibility видимым или нет («PhoneLightTheme», который также существует, будет иметь противоположное значение). Если это так, мы теперь, какая текущая тема. Мы можем сделать 2 вещи, в зависимости от типа используемых кнопок:

  • Загрузите в новом стиле для ваших элементов управления.
  • Работа с изображениями, которые заменяют задний и передний план кнопок.

Загрузить новый стиль

Много было написано о том, как изменить тему ваших элементов управления во время выполнения, так что в целом, что нужно сделать, это что-то вроде:

  1. Создайте 2 кисти или стили, по одной для каждой темы, и поместите их в ресурсы своего приложения (файл App.xaml)

  2. Когда вам нужно перейти на другую тему, удалите текущую кисть или стиль из вашего приложения, например:
    App.Current.Resources.Remove("MyCurrentTheme");

  3. Вставьте другую кисть или тему в ваше приложение, например:
    App.Current.Resource.Add("MyCurrentTheme", mydarkTheme)

Изображения и темы

Проблема, описанная ранее с кнопками, выглядящими как некрасивая Бетти, также относится к изображениям. Предположим, вы создали красивое прозрачное изображение, которое будет использоваться в вашей кнопке:

Черная часть изображения прозрачна. Использование этой кнопки в темной теме дает приятные результаты:

<Button>
component/home_white.png">
</Button>

 

На самом деле, благодаря прозрачности, мы можем делать такие забавные вещи, как:

<Button Background="Green">
component/home_white.png">
</Button>

 

Однако, если мы явно не определили фон (зачем вам это? Помните, дизайн Metro — это все, что не связано с хромом и другими глупыми неиспользуемыми изменениями пользовательского интерфейса… проверьте ваш iPhone или Android, чтобы понять, что я имею в виду: p), а затем переключая темы, мы снова создали очень белый прямоугольник:

Мы можем решить эту проблему двумя способами:

  1. Различный набор изображений для обеих тем
  2. Есть один набор, но игрушка с прозрачностью

Наборы изображений

Наличие двух наборов изображений позволяет точно знать, как каждая кнопка (или элемент управления) будет выглядеть в каждой теме. Недостатком является то, что каждый раз, когда вы меняете одно изображение, вам также приходится менять другое. Тем не менее, время от времени так и происходит. Для работы с наборами изображений самое простое решение, отличающееся от этого , выглядит следующим образом:

  1. Создайте 2 набора изображений и включите их как ресурсы, контент или ресурсы в ваши решения. Оставьте все имена одинаковыми, но измените только папку, в которой они находятся (например, «кнопки / темные /» и «кнопки / светлые»).
  2. При инициализации страницы или приложения, запросите телефон, какая тема используется. (см. ранее).
  3. Загрузите в правильный набор изображений

Как это сделать на практике? Предположим, что мы определили кнопку где-то в нашем интерфейсе следующим образом:

<Button >
<Image x:Name="btnImage" ></Image>
</Button>

 

Следующий код, который может быть размещен в конструкторе страницы, может быть:

Uri uri;
if ((Visibility)Application.Current.Resources["PhoneDarkThemeVisibility"] == Visibility.Visible)
uri = new Uri("/buttons/dark/home.png", UriKind.Relative);
else
uri = new Uri("/buttons/light/home.png", UriKind.Relative);
btnImage.Source= new BitmapImage(uri) ;

 

Обратите внимание, что мы могли бы также легко реорганизовать этот код для определения обеих строк папок-папок («button / dark /» и «button / light /») в другом месте и только один раз написать имя файла («home.png»);

Один набор изображений, меняя прозрачность

Для тех из нас, как я, которым не очень нравится играть в образы, существует третий вариант решения проблемы меняющихся тем, объясненный здесь и здесь (первый ответ). Это решение, которое я предпочитаю выше описанных ранее, выглядит следующим образом:

  1. Создайте единый набор черно-белых изображений
  2. Реализуйте стиль, который меняет черный / белый цвета изображений в соответствии с активной темой (на практике: измените активную прозрачность с черного на белый или наоборот)
  3. Применить стиль ко всем элементам управления

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

На практике необходимы 2 вещи:

Сначала создается новый стиль, который затем можно применить к любой кнопке в приложении. Вкратце (полная версия здесь ):

<phone:PhoneApplicationPage.Resources>
<Style x:Key="IconButton" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{StaticResource PhoneForegroundBrush}"/>
<Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
<Setter Property="BorderThickness" Value="{StaticResource PhoneBorderThickness}"/>
<Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilySemiBold}"/>
<Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMediumLarge}"/>
<Setter Property="Padding" Value="10,3,10,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Background="Transparent">
<VisualStateManager.VisualStateGroups>
………….
</VisualStateManager.VisualStateGroups>
<Border x:Name="ButtonBackground" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="0" Margin="{StaticResource PhoneTouchTargetOverhang}">
<Grid x:Name="ContentContainer" OpacityMask="{TemplateBinding Content}" Background="{TemplateBinding Foreground}"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

 

А пока давайте рассмотрим основной трюк в стиле:

<Grid x:Name="ContentContainer" OpacityMask="{TemplateBinding Content}" Background="{TemplateBinding Foreground}"/>

Здесь мы создали в основном новый тип Button, который использует ImageBrush в качестве своего содержимого, содержащего изображение, которое мы хотим показать на кнопке. Тем не менее, стиль гарантирует, что мы используем альфа-маску, которая использует изображение в качестве маски (поэтому убедитесь, что ваше изображение действительно имеет альфа-канал; Совет: используйте PNG!). Устанавливая цвет фона на текущий цвет переднего плана (в зависимости от выбранной темы), мы гарантируем, что непрозрачная часть изображения будет иметь правильный цвет (что просто происходит, если мы «вырезали» изображение с переднего плана) , используя альфа-маску).

Теперь осталось только применить этот стиль к нашим элементам управления (кнопки в этом примере):

<Button Style="{StaticResource IconButton}" >
<ImageBrush ImageSource="/icons/home.png">
</Button>

 

Если мы сейчас протестируем наше приложение, то увидим, что кнопка отображается правильно, независимо от активной темы. Картинка слева показывает темную тему:

 

Источник: http://timdams.wordpress.com/2011/06/21/creating-a-wp7-app-supporting-dark-and-light-themes/