Я пишу приложение-клиент под Windows Phone для одного стартапа, по просьбе заказчика взяв за образец дизайна уже готовое приложение для iPhone. Анонс приложения состоится несколько позже, когда оно будет готово и доступно для загрузки, а в сегодняшней статье я хотел бы рассказать о том, как решил один из вопросов дизайна.
В приложении-образце в нескольких местах используется элемент управления UISegmentedControl из iOS. У него есть несколько вариантов отображения, но в данном приложении используется примерно вот такой:
Моя проблема заключалась в том, что на Windows Phone не существует даже похожего элемента управления. Разумеется, приложения под разные платформы ни в коем случае не должны копировать друг друга, а должны соответствовать гайдлайнам под ту или иную платформу. Однако данный элемент управления мне показался интересным, и я решил придумать свою вариацию на тему SegmentedControl в Windows Phone.
Идея
Единственное, что практически сразу пришло в голову, — это должны быть RadioButton, каким-то образом стилизованные. Но я абсолютно не представлял себе, как это должно выглядеть. В интерфейсе Windows Phone, по большому счёту, нет понятия объёмных элементов управления, поэтому SegmentedControl в том виде, в каком он присутствует на iOS — активная кнопка «вдавлена», а неактивные нет, — совершенно не подходит для интерфейса WP.
В процессе поиска ответа я наткнулся на статью о том, как реализовать на Windows Phone CheckBox/RadioButton в стиле Windows 8, то есть что-то подобное:
Такой вариант мне очень понравился, поскольку он, во-первых, достаточно приятно выглядит, а во-вторых, не противоречит дизайну WP, поскольку используется как минимум при выборе фотографий в галерее:
Таким образом, нужно было стилизовать RadioButton в подобном виде с некоторыми изменениями, которые отвечали бы моим потребностям. А поскольку к указанной статье автор любезно прикрепил XAML с получившимися у него стилями, я решил воспользоваться ими (тем более, что до дизайнера мне, пожалуй, далеко).
Как оказалось, в позаимствованных стилях прямоугольная обводка RadioButton акцентным цветом появляется вместе с уголком и галкой только при выборе, т.е. если RadioButton не выбрана, то обводки вокруг неё нет:
К тому же, мне хотелось изменить и цвет текста на неактивных кнопках, чтобы он был… ну, неактивным.
Реализация
Поскольку мне нужна была только RadioButton, то стили для CheckBox из заимствованного XAML я тут же удалил… Поэтому если кому-то они будут нужны, то в моей реализации, приложенной к статье чуть ниже, вы их не найдёте. Но я постараюсь подробнее описать, что же я поменял в заимствованном стиле для RadioButton, так что воспроизвести это для CheckBox не составит труда.
Сначала нужна была обводка для неактивной кнопки, поэтому в стиле, в Grid (сетке) с именем «CheckElements», появился такой вот Border, по образу и подобию обводки для активной кнопки:
<Border x:Name="NormalBorder" BorderThickness="3" BorderBrush="{StaticResource PhoneDisabledBrush}" />
<Border.BorderBrush> <SolidColorBrush Color="{StaticResource PhoneAccentColor}"/> </Border.BorderBrush>
— ведь вместо этого великолепия можно было использовать ресурс кисти и написать просто BorderBrush="{StaticResource PhoneAccentBrush}"
.
Кроме того, для состояния Disabled, когда RadioButton не выбрана, я добавил анимацию (по сути просто триггер) перекрашивания текста кнопки в неактивный цвет:
<VisualState x:Name="Disabled"> <Storyboard> ... <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer"> <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState>
Вдобавок я удалил все упоминания эллипсов под названиями «CheckBackground» и «CheckMark», поскольку они оставались от оригинального стиля RadioButton и в новом стиле были не нужны. Это же позволило убрать разделение на колонки в Grid с содержимым кнопки.
IsChecked="false"
) и если эта RadioButton не находилась на одной строке с выбранными кнопками, её высота была меньше, чем высота выбранной кнопки. Поскольку изначально автор этих стилей хотел создать радио-кнопки для картинок, как, например, в галерее Windows Phone, для него это проблемой не было, но для меня, в качестве содержимого RadioButton задающего только текст, это было неприемлемо. Сначала я добавил ещё один Border, служащий «распоркой» в случае, если высота RadioButton меньше высоты уголка с галкой, но потом необходимость в нём отпала… Сейчас, при написании этой статьи, я понимаю, что дальше совершил ошибку. Но поскольку опыта в стилях у меня не так много (повторюсь, от дизайна я ещё далёк), я взял да и удалил все упоминания об элементе Border с именем «border_copy», заменив его на, как мне показалось, абсолютно такой же просто «border». Это было ошибкой, но это позволило мне впоследствии ещё раз переосмыслить стиль RadioButton’а, который я хотел бы применить.
Результаты и ошибки
После осуществления этих манипуляций. изображённый выше элемент управления приобрёл следующий вид:
В дизайнере Visual Studio всё выглядело отлично, однако тесты на аппарате показали, что если нажать на уже активный RadioButton, цветная обводка на нём пропадает и из элементов активной кнопки остаётся только уголок с галкой. Это как раз и стало результатом ошибки с удалением «border_copy»: он использовался в визуальном состоянии Pressed, в то время как просто «border» был задействован в состоянии Checked.
Всё дело в том, что в стилях XAML нельзя дважды задать одно и то же значение одному и тому же свойству одного и того же элемента. В моей ситуации происходило следующее: в состоянии Checked элементу «border» устанавливался параметр Visibility="Visible"
, а при повторном нажатии на активную кнопку в состоянии Pressed происходило то же самое! Это и приводило к ошибке.
Пока я это выяснял, я обнаружил, что стиль написан не очень «красиво»: много лишнего. Во-первых, в каких-либо состояниях задавать элементам их исходные свойства нет необходимости. Т.е., если у элемента «border» изначально задано Visibility="Collapsed"
, то в состоянии Unchecked задавать ему заново такое же значение просто не нужно. Это позволило здорово «причесать» код. Во-вторых, судя по примеру на MSDN, в оригинальном стиле для сокрытия и отображения элементов используется не свойство Visibility="Collapsed"/"Visible"
, а свойство Opacity=0/1
. Это позволило ещё больше облагородить код за счёт использования DoubleAnimation вместо ObjectAnimationUsingKeyFrames, а также отказаться от использования «распорки», описанной под спойлером выше, поскольку Path уголка с галкой всегда находился где-то рядом, только был прозрачен.
В итоге появилась одна обводка для состояния Pressed, которая после небольшого переосмысления стала выглядеть так:
<Border x:Name="PressedBorder" BorderThickness="3" BorderBrush="{TemplateBinding Foreground}" Opacity="0" />
Переосмысление коснулось в том числе и свойства BorderBrush
, которое стало привязано не к акцентному цвету системы, а к цвету Foreground радио-кнопки.
Наконец, поскольку в Windows Phone 7.x нет ресурса «PhoneRadioCheckBoxBorderBrush», я заменил его на «PhoneForegroundBrush», поскольку «PhoneRadioCheckBoxBorderBrush» в WP8 — это кисть цвета «PhoneForegroundColor».
В конечном итоге получился вполне рабочий и, как мне кажется, даже аккуратный стиль, позволивший мне создать подобие SegmentedControl для Windows Phone. Да, у каждой радио-кнопки задано свойство Margin="-12,0"
, чтобы они плотно прилегали друг к другу.
ссылка на оригинал статьи http://habrahabr.ru/post/181348/
Добавить комментарий