Клиент ВсеМое.ру – логин

Скорей, надо скорей писать код! Как театр начинается с вешалки, так и любой клиент ВсёМоё начинается с окошка логина. Надо его нарисовать!

Для начала выдираем скриншотом, прямо из эмулятора, работащий логин.

Screen Shot 2014-03-30 at 14.57.55

В оригинале всё, кроме полей ввода и чекбокса, нарисовано прямо на бэкграунде. С одной стороны, это хорошо и уменьшает объем кода. А с другой стороны, ограничивает нарисованным. А ведь охота нарисовать сразу для всех платформ. Ну или практически для всех …

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

В общем, как-то так получается в реальности.

Screen Shot 2014-03-30 at 15.52.12

Берем Qt Creator и создаем в нем проект, который использует QtQuick.Controls. Мне он поугрожал, что использовать полученное можно будет только с версией iOS больше 5.1 … но найдите мне такую версию живьем?

Создаем в нем фаил ресурсов и загоняем туда картинки, тупо выдранные из оригинала (красоту будем наводить потом).

Для начала нам надо добиться, что бы один и тот же код выполнялся и под десктопом и под айфончиком.

Для этого надо:

а) загнать qml в ресурсы
б) нафиг сменить процедуру загрузки, иначе будут ошибки про not ready и прочее. Короче QTBUG-29873

main.cpp сейчас у меня выглядит так

Screen Shot 2014-03-30 at 17.24.36

Красивая картинка? Полностью полученное можно посмотреть где положено (то есть в git и нефиг ныть!), а у меня получилось следующее:

Screen Shot 2014-03-30 at 17.21.06

Поверьте, на андроиде примерно тоже самое. Теперь необходимо осуществить … гламуризацию всего этого дела. Например, где кнопочка “регистрация” (и нужна ли она)? И все криво при повороте набок. И пароль набирается видимыми буквами. И нету чекбокса. И вообще, все криво.

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

Для начала убираем всякие Column { и тупо прописываем все подряд. Затем добавляем State почти по инструкции http://doc.qt.digia.com/qtquick-components-symbian-1.1/qt-components-scalability-guidelines-orientation-specific-layouts.html

Так как там немного древнее, я заиспользовал одну StateGroup, без отдельной с when:true


StateGroup {
states: [
State {
when: (main.width / main.height) < 1.00

PropertyChanges {
target: loginEmail; text: "portrait"
}

AnchorChanges { target: logoImage; anchors.top: parent.top }
AnchorChanges { target: loginEmail; anchors.top: logoImage.bottom }
// AnchorChanges { target: userPassword; anchors.top: loginEmail.bottom }
},
State {
when: (main.width / main.height) > 1.00

PropertyChanges {
target: loginEmail; text: "landscape"
}
AnchorChanges { target: loginEmail; anchors.top: logoImage.bottom }
// AnchorChanges { target: userPassword; anchors.left: loginEmail.right }
}
]
}

Для проверки я тупо убрал лишнее и в поле для логина начал выводить, какой же State используется. И потом, с помощью PropertyChanges и AnchorChanges я меняю свойства и привязки элементов. В итоге получается “адаптивный” дизайн.

Вот теперь можно и добавить кнопки про регистрацию и вперде!

Правда, вперде прямо сразу не получится. По двум причинам.

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

Во-вторых, это долбанное разнообразие разрешений и форматов экрана. У кого-то 4:3, а у кого-то 16:9. Кому-то хватает 640х480 или даже 320 на 240, а кому-то и 1920 на 1080 не хватает.

Что делать?

После некоторого раздумья я решил, что фиг с этими пропорциями. Пусть будет просто две ориентации экрана: горизонтальная и вертикальная.

Для определения платформы (мобильная или десктопная) проще всего использовать предопределенные константы прямо из Qt. напрямую из QML не получится, слишком кроссплатформенный он получился …

Итак, что имеем?

1. ориентацию экрана легко узнать из самого QML. Просто по соотношению высоты и ширины экрана.
2. Ориентацию элементов управления (для мобильного прижимаем кверху, а для десктопа – центрируем) передаем в QML снаружи. Делать свой QML для каждой платформы – моветон.
3. Для удобства позиционирования волевым решением принимаю, что экран в горизонтальной ориентации в реальности имеет размеры 40х30. Кто-то это называет device independed pixels, кто units, кто еще как. И все координаты будут преобразовываться в реальные пикселы отдельной функцией. Сначала засуну ее в QML, а там посмотрим.

Ну, пункт 1 сделан еще на подготовительном этапе.

Теперь надо передать платформу в QML. Обычно народ сразу хватается за классы, но нам-то по идее хватит что-нибудь типа 0 – по центру, 1 – прижимать все вверх.

Для этого тупо устанавливаем переменную для QML

engine.rootContext()->setContextProperty(“platform”,31);

Теперь везде можно использовать platform. Ну и для порядку неплохо бы обернуть все это в обертку из #define

int platform=0;

#ifdef Q_OS_ANDROID
platform=1;
#endif

#ifdef Q_OS_IOS
platform=1;
#endif

#ifdef Q_OS_WINPHONE
platform=1;
#endif

Потом как-нибудь можно будет сделать более тонкое разделение по платформам.

Что касается пикселов, то это решается еще проще.

Где-нибудь в начале QML, сразу после ApplicationWindow вставляю следующее:

property int guxSize: width/40
property int guySize: height/30

function gux(c)
{
return guxSize*c;
}
function guy(c)
{
return guySize*c;
}

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

Ну а теперь пора переходить к определению элементов и их размеров. При этом крайне рекомендую внутри элемента описаться на его ширину/высоту, а не на guy/gux. Просто потом будет гораздо легче. И никаких pointSize! Только pixelSize.

Как это выглядит в коде, вы можете увидеть в Stage1. В реальности это выглядит так:

Для “десктопа”

landscape

Для “портретного” режима

portrait

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

И да, можно загрузить полученное в телефончик/планшетик и посмотреть, как оно будет выглядеть там. Ну и похвастаться кому-нибудь из знакомых. Всё-таки у вас в руках по настоящему кросс-платформенное приложение.