Jump to content

Yaroslav Brovin

Administrators
  • Posts

    2,567
  • Joined

  • Last visited

  • Days Won

    647

Blog Entries posted by Yaroslav Brovin

  1. Yaroslav Brovin
    Добрый день, уважаемые пользователи FGX Native!
    Вот мы и подошли к главной ключевой новинке версии FGX Native 1.15.0.0, а именно речь пойдет о новой системе сборки для Android приложений 🔥. Долгое время мы шли к этому решению и уверены, что данная система сборки является ключевым нововведением для всех будущих релизов FGX Native. Это огромный шаг и потенциал для мобильной разработки на Delphi.
    Перед тем, как окунуться в детали новинки, настоятельно рекомендуем ознакомиться с нашей статьей о миграции на AndroidX, в которой мы частично описали текущие проблемы, которые стоят за штатной системой сборки Delphi.
    Что не так со стандартной системой сборки Delphi для Android приложений?
    Для начала нужно понять, почему в чём проблемы стандартной системы сборки.
    1. Поддержка сторонних Android-библиотек
    Это самая важная часть для FGX Native. Мы используем различные сторонние Android библиотеки - Firebase, AdMob, Visio и прочие. Все эти библиотеки в свою очередь используют другие библиотеки, и тд т тп. Каждая библиотека - это не только код, но и ресурсы, изменения необходимые для Android манифеста и тд. Соотвественно, для полноценной поддержки сторонних Android-библиотек требуется не только внедрить исполняемый код, но и так же поставить все ресурсы и слить правки в Android манифесте.
    Стандартная система сборки Delphi ограничена только внедрением кода. А это значит, что выдрав из Андроид-библиотеки только код и проигнорировав ресурсы, манифесты и нативный код, мы получаем нестабильное решение, которое может не работать, падать и тд. Поэтому в Delphi сообществе очень много проблем с использованием сторонних Android решений. И все они зачастую упираются в ограничение стандартной системы сборки.
    Более подробно проблема поддержки Android-библиотек описана в статье про миграцию на AndroidX. 2. Скорость
    Не секрет, что скорость сборки Android приложений из Delphi, по сравнению с нативными средствами разработки, оставляет желать лучшего.  И если во время сборки простого приложения проблема не так заметна, то ситуация кардинально менятся, когда мы имеем дело с приложениями готовыми для продакшена и содержащими большое количество кода, ресурсов и тд.
    Повторный запуск приложения (после сборки без изменений) может занимать минуты(!) (в зависимости от используемых ресурсов). А после внесения любых, даже самых незначительных изменений в проект, требуется потратить почти столько же времени, как на сборку проекта с нуля.
    Мы собрали немного статистики от наших клиентов по сборке существующих продакшен-приложений с большим количеством экранов и ресурсов. Данные показатели зависят от многих факторов: сложность проекта, характеристики  компьютера, на котором осуществляется сборка и тд. Но для нас важно относительные значения времени в рамках одного проекта и одного компьютера. Поэтому следует иметь ввиду, что в каждом конкретном случае числа будут другими.

    Первый столбец содержит время старта (без отладки) проекта с нуля. До этого проект не собирался и нет никаких промежуточных результатов сборки. Второй столбец указывает время старта (без отладки) проекта после внесения изменения в проект: изменили исходный код и тд. Третий столбец отвечает за повторный старт (без отладки) приложения без внесения изменений .     Из этой таблицы хорошо видно, что в среднем на сборку с запуском уходит ~1 минута. Обычный сценарий использования: внес изменений в коде, собрал, запустил, поймал ошибку, опять внес изменений и тд. Грубо говоря, в среднем 60 запусков в день приложения (без отладки) может занимать 1 час рабочего времени! С отладкой цифры становятся еще более угрожающими. Тратится драгоценное время на ожидание, вместо того, чтобы потратить его с пользой.
    3. Технологическое отставание от требований Google
    Стоит отметить, что Google активно развивает сборку Android приложений, каждый раз выдвигая всё новые и новые требования к полученному приложению:
    Был APK, на его смену пришел более умный AAB. Новые требования к версии используемых библиотек. Если используемая библиотека старая, то приложение не может быть загружено в маркет или же просто не работать на свежих версиях Android. Поскольку Delphi самостоятельно реализует систему сборки, то получается состояние вечной гонки, в которой Delphi, к сожалению, всегда в роли догоняющего. Поэтому, как только Google/Apple меняют структуру пакета приложения, вам приходится ждать обновлений Delphi, которые изменят встроенную систему сборки в соответствии с новыми требованиями. С учётом частоты выхода новых версий Delphi это может вылиться в длительное ожидание, а иногда и финансовые затраты, необходимые для приобретения новой версии.
    Что нам дает новая система сборки
    Учитывая все перечисленные выше минусы встроенной сборки Android приложений, мы пошли по единственному верному на наш взгляд пути, а именно, мы внедрили родную систему сборки, которая используется в Android Studio и которую рекомендует сам Google.
    1. Поддержка Android-библиотек
    Полноценная поддержка всех Android-библиотек. Теперь перед Delphi разработчиком не будет практических никаких ограничений для использования сторонних библиотек. Мы предлагаем полный цикл интеграции любого стороннего решения в FGX Native приложения под Android:
    Вы находите интересуемое решение на Java для Android Вы добавляете Java библиотеку в FGX Native проект Вы генерируете заголовочные файлы для Java библиотеки на языке Delphi при помощи нашей утилиты Java2Delphi. В будущем появится автоматическое скачивания всех зависимостей. Так что вам даже не придется искать файл библиотеки, достаточно будет указать имя библиотеки и её версию. Остальное на себя возьмет FGX Native.
    2. Скорость
    После внедрения новой системы сборки, мы попросили наших клиентов, повторно протестировать сборку их проектов. Все результаты сведены в таблицу ниже. Из нее наглядно видно, что в целом сборка и запуск  проектов стал быстрее от 2 до 5 раз 🔥(кроме старта с нуля, что обуславливается долгой сборкой главной so-библиотеки приложения, которая целиком и полностью зависит от компилятора и линкера).

    Давайте разберем результаты подробнее.
    Старт после изменений
    Данный замер является самым часто используемым. Вы вносите изменения в проект и запускаете приложение для тестирования и отладки. Практически - это основной сценарий работы разработчика. В целом прирост скорости составляет от 2 до 5 раз на тестируемой выборке проектов 🚀. Важно отметить, что при этом новая система сборки проделывает гораздо большую работу по сравнению со стандартной, даже если брать в расчёт только внедрение дополнительных ресурсов от используемых библиотеки. Количество ресурсов может исчисляться сотнями, и, если добавлять их все через Deployment Manager, то разница в результатах будет еще больше (не в пользу стандартной системы сборки). 

    Повторный старт
    Данный вариант используется реже, когда вы пытаетесь воспроизвести какую-то ситуацию без изменений кода и у вас не получается ее поймать с первого раза. В этом случае вы просто запускаете приложение из IDE повторно. 
    В этом случае показатели скорости просто ошеломительные. Прирост составляет в ~5 раз на текущей выборке и практически он является постоянной. Если в стандартной системе сборке повторный запуск зависит от количества ресурсов и размера приложения, то для FGX Native это постоянная величина.

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

    3. Технологическое отставание от требований Google
    Мы не "изобретаем велосипед" в сборке Android приложений, а берем проверенное официальное решение от Google, которое поддерживается Google. А это значит:
    Соответствование всем новым требованиям со стороны Google Легкое/моментальное обновление системы сборки до последней версии, без необходимости обновления Delphi Использование
    Первая сборка
    При первом открытии FGX Native проекта, начиная с 1.15.0.0 автоматически будет выполнена миграция настроек проекта, в рамках которой будет предложено обновить шаблон манифеста Android приложения и файл стилей styles.xml. Если вы не вносили никаких правок, просто подтвердите замену. Если же вы вносили правки, то необходимо перенести их  вручную в правую часть редактора.
    При обновлении в корень проекта автоматически сохраняется старый файл шаблона. Данное поведение можно отключить при помощи чекбокса, расположенного в левом нижнем углу.

    При первой сборке FGX Native проекта начиная с 1.15.0.0 в корне проекта автоматически будет создана директория ".android-build". Этот каталог содержит всё необходимое для сборки вашего проекта. Его не нужно добавлять в систему контроля версии, он может быть большого размера, так как там сохраняются промежуточные результаты сборки, при необходимости его можно удалять. Все файлы внутри данной директории являются автогенерируемыми, поэтому не пытайтесь вносить ручные правки, они будут проигнорированы. 
    Первая сборка может занять длительное время, так как при ней загружаются с интернета (требуется наличие активного интернет соединения!) все необходимые компоненты сборки. Однако последующие сборки будут проходить намного быстрее. Во время первой сборки среда может визуально "подвисать", это связано с тем, что интеграция с msbuild в IDE не умеет выполняться полностью параллельно.
    Ошибки сборки
    Если в результате сборки появляется ошибка, то подробную информацию можно найти на нижней вкладке "Output", а не в "Build", как раньше.
    Например, все ошибки упаковки на панели "Build" будут сводиться к такой.

    Подробности можно найти на вкладке "Output". По тексту ошибки становится ясно, что в проекте используется TargetApiVersion ниже 33.

    Требования
    Для успешной сборки требуется свежая версия Java JDK (не ниже 18). Можно использовать ту, что идет в поставке с Delphi. Требуется наличие интернета для первой сборки приложения, так как при ней скачиваются необходимые компоненты сборки. Теперь все FGX Native под Android используют TargetAPILevel = 33. Мы полностью перешли на использование последней версии Android. Поэтому понижать версию теперь нельзя. Когда планируется релиз?
    Поскольку релиз 1.15.0.0 является очень крупным и затрагивает критические важные моменты, мы хотим быть уверены, что он не испортит ваши проекты. Поэтому внедрение будет происходить постепенно. На первом этапе мы отдали некоторым нашим клиентам сборку на предварительное тестирования на больших проектах, получили обратную связь и устраняем найденные недочеты.  Далее на этой неделе мы планируем выпустить экспериментальную версию 1.15.0.0 для широкого круга пользователей. По сути - это полноценная версия и вы сможете опробовать её для перехода на Android 13, сравнить скорость сборки и тд. Однако, если вы столкнетесь с какими-то серьезными проблемами для вашего проекта, вы всегда сможете откатиться на стабильную версию 1.14.6.0. На текущий момент, практически все большие собранные проекты на 1.15.0.0 успешно запускаются и работают.
     
    Если при тестировании вы столкнетесь с проблемами, просьба сообщить нам о них, и мы постараемся их оперативно поправить.
    Всем спасибо за внимание и хорошей продуктивной недели!
  2. Yaroslav Brovin
    В эту среду совместно с компанией Embarcadero проводим вебинар посвященный разработке мобильного приложения с использованием FGX Native. На вебинаре разберем одну из возможных архитектур мобильного приложения, применим шаблон MVVM для построения экранов. И познакомимся с приемами создания мобильных интерфейсов с FGX Native. 
    Когда: 23 июня 2021 в 12:00 МСК Язык: русский Заголовок: Примеры использования библиотеки FGX Native для создания макетов реальных мобильных приложений. Ссылка на регистрацию: https://lp.embarcadero.com/fgx-native Анонс: 
  3. Yaroslav Brovin
    Продолжаем серию статей, посвященных новинкам релиза FGX Native 1.11.0.0. И в этой статье поговорим о самой главной и ожидаемой новинке - поддержка полноценных анимаций 🔥🔥🔥.
     
    TfgAnimationManager - центр управления анимациями
    Главным центром управления анимации любого компонента является менеджер анимаций, доступ к которому можно получить через свойство TfgControl.AnimationManager. Именно он отвечает за создание, добавление и удаление аниматоров.
    Создание аниматора
    Чтобы создать нужный аниматор нужно воспользоваться одним из следующих методов TfgAnimationManager:
    function AddOpacityAnimation(const AStartOpacity, AFinishOpacity: Single; const ADuration: Integer = TfgAnimation.DurationPlatformValue): TfgAnimation; function AddBoundsAnimation(const AStartBounds, AFinishBounds: TRectF; const ADuration: Integer = TfgAnimation.DurationPlatformValue): TfgAnimation; function AddScaleAnimation(const AStartScaleX, AFinishScaleX, AStartScaleY, AFinishScaleY: Single; const ADuration: Integer = TfgAnimation.DurationPlatformValue): TfgAnimation; function AddRotationAnimation(const AStartAngle, ASweepAngle: Single; const ADuration: Integer = TfgAnimation.DurationPlatformValue): TfgAnimation; function AddTranslationAnimation(const AStartPoint, AFinishPoint: TPointF; const ADuration: Integer = TfgAnimation.DurationPlatformValue): TfgAnimation; function AddGroupAnimation: TfgAnimationGroup; Каждый метод создает соответствующий своему названию тип аниматора с указанными стартовыми и конечными значениями. Например, если вы хотите плавно показать кнопку в течении 1 секунды, достаточно выполнить следующий код.
    uses FGX.Animation; var Animation := Button.AnimationManager.AddOpacityAnimation(0 {start opacity}, 1 {finish opacity}, 1000 {msec}); Animation.Start; Обратите внимание, что в данном случае, созданный аниматор "Animation" останется у компонента и не будет удален, пока вы не сделаете это сами или компонент не будет уничтожен. Если вы планируете часто использовать один и тот же вид анимации на конкретном компоненте, то предпочтительным способом будет являться именование анимации. Например:
    uses FGX.Animaton.Helpers; // Создаем аниматор один раз Button.AnimationManager.AddOpacityAnimation(0 {start opacity}, 1 {finish opacity}, 1000 {msec}) .SetName('fade-in'); // Будущие запуски анимации Button.AnimationManager['fade-in'].Start; Если же вам достаточно выполнить анимацию всего лишь один раз, то имеет смысл пометить ее для автоматического удаления опцией TfgAnimationOption.ReleaseOnFinish: 
    uses FGX.Animation.Types, FGX.Animation.Helpers; Button.AnimationManager.AddOpacityAnimation(0 {start opacity}, 1 {finish opacity}, 1000 {msec}) .AddOption(TfgAnimationOption.ReleaseAnimationOnFinish) .Start; Хелпер анимации
    Для удобного создания аниматоров мы предлагаем вам готовый хелпер FGX.Animation.Helpers, который позволяет использовать цепочку вызовов методов аниматора и облегчить визуальное восприятие готового кода.
    Без хелпера:
    uses FGX.Animation; var OpacityAnimation: TfgAnimation; OpacityAnimation := Button.AnimationManager.AddOpacityAnimation(0 {start opacity}, 1 {finish opacity}); OpacityAnimation.Duration := 1000 {msec}; OpacityAnimation.AutoReverse := True; OpacityAnimation.Delay := 500 {msec}; OpacityAnimation.Options := OpacityAnimation.Options + [TfgAnimationOption.ReleaseAnimationOnFinish]; OpacityAnimation.Start; С хелпером:
    uses FGX.Animation.Types, FGX.Animation.Helpers; Button.AnimationManager.AddOpacityAnimation(0 {start opacity}) .SetDuration(1000 {msec}) .SetDelay(500 {msec}) .SetAutoReverse .SetReleaseAnimationOnFinish .Start; Чтобы воспользоваться хелперами, необходимо подключить модуль FGX.Animation.Helpers.
    Обзор базовых аниматоров
    Поддерживается 5 базовых типов анимаций:
    Opacity. Изменение прозрачности (TfgControl.Opacity). Bounds. Изменение положения и размера (TfgControl.Position + TfgControl.Size).  Scale. Изменение масштаба отображения компонента. Rotation. Изменение угла поворота компонента. Translate. Выполнение смещения компонента относительно его текущей позиции (TfgControl.Position). Анимация прозрачности (TfgAnimationManager.AddOpacityAnimation)
    Чтобы выполнить анимацию прозрачности нужно создать аниматор через метод TfgAnimationManager.AddOpacityAnimation и указать стартовое и конечное значение свойства TfgControl.Opacity. 
    Image.AnimationManager.AddOpacityAnimation(0 {start opacity}, 1 {finish opacity}, 2000 {msec}) .Start; opacity.mp4
    Анимация перемещения и изменения размера (TfgAnimationManager.AddBoundsAnimation)
    Чтобы выполнить анимацию изменения размера и позиции нужно создать аниматор через метод TfgAnimationManager.AddBoundsAnimation и указать стартовый и конечный прямоугольник. Пример ниже демонстрирует интересный прием акцентирования внимания на компоненте через визуальное расширение и сжатие компонента.
    var StartRect: TRectF; StopRect: TRectF; begin StopRect := imgPhoto.Bounds; StartRect := imgPhoto.Bounds; StartRect.Inflate(16, 16); Image.AnimationManager.AddBoundsAnimation(StartRect, StopRect) .Start; bounds.mp4
    Анимация изменения масштаба (TfgAnimationManager.AddScaleAnimation)
    Чтобы выполнить анимацию изменения масштаба нужно создать аниматор через метод TfgAnimationManager.AddScaleAnimation и указать стартовые и конечные значения коэффициентов масштабирования по горизонтали и вертикали.
    Image.AnimationManager.AddScaleAnimation(0.5, 1, 0.5, 1, 1000) .Start; scale.mp4
    Анимация поворота компонента (TfgAnimationManager.AddRotationAnimation)
    Чтобы выполнить анимацию поворота компонента нужно создать аниматор через метод TfgAnimationManager.AddRotationAnimation и указать стартовое значение угла и на сколько градусов нужно совершить поворот (в градусах). При этом, если угол поворота положительный, то поворот осуществляется по часовой стрелки, а если отрицательный, то против часовой стрелки.
    Image.AnimationManager.AddRotationAnimation(0 {start}, 360 {Sweep}, 1000) .Start; rotate.mp4
    Анимация смещения (TfgAnimationManager.AddTranslationAnimation)
    Чтобы выполнить анимацию смещения компонента нужно создать аниматор через метод TfgAnimationManager.AddTranslationAnimation и указать стартовое и конечное смещения значения позиции компонента.
    Image.AnimationManager.AddTranslationAnimation(TPointF.Create(-16, 0), TPointF.Create(16, 0), 1000) .Start; translate.mp4
    TfgAnimation - основа анимации
    После того, как мы посмотрели несколько примеров создания аниматоров вживую, пришло время рассмотреть их настройки детальнее.
    Длительность анимации (TfgAnimation.Duration)
    Общая длительность выполнения текущей анимации в миллисекундах в одну сторону (от стартового значения до конечного).
    Задержка перед запуском (TfgAnimation.Delay)
    Если вам необходимо выполнить анимацию с задержкой, то вы можете это сделать указав значение задержки в миллисекундах в свойстве TfgAnimation.Delay.
    Количество повторений (TfgAnimation.RepeatCount)
    Если вам необходимо повторить анимацию несколько раз или сделать ее бесконечной, то вы можете это сделать при помощи этого свойства, где
    RepeatCount = 0 - повторение анимации бесконечное число раз. RepeatCount > 0 - повторение анимации ровно "RepeatCount" раз. Например при помощи этого свойства легко сделать анимацию тряски: повторим 4 раза движение компонента по горизонтальной оси.
    shake+repeatCount.mp4
    Интерполяция (TfgAnimation.CurveKind)
    Это свойство отвечает за формулу интерполяции значений анимируемых свойств. Пока поддерживается 4 самых распространенных типов интерполяции.
    TfgAnimationCurveKind = (Linear, EaseIn, EaseOut, EaseInOut);
    Краткое описание стратегии изменения анимируемого значения на протяжении всей длительности анимации в зависимости от типа интерполяции:
    Linear - равномерное изменение. Варианты типичного применения: короткие анимации, изменение цвета. EaseIn - медленное начало с постепенным нарастанием скорости к концу анимации. Варианты типичного применения: исчезновения объекта. EaseOut - быстрое начало с постепенным замедлением скорости к концу анимации. Варианты типичного применения: появления объекта. EaseInOut - медленное начало с нарастанием скорости посередине и постепенным замедлением скорости к концу анимации. Варианты типичного применения: перемещение объекта, изменение масштаба. Порядок запуска (TfgAnimation.RunOrder)
    Это свойство актуально только в контексте групповых аниматоров и позволяет указывать порядок запуска текущей анимации в группе. Подробнее это будет рассмотрено в разделе "Групповые анимации".
    TfgAnimationRunOrder.Immediately - запуск анимации в момент старта родительского группового аниматора, либо вместо с предыдущим одноуровневым аниматором. TfgAnimationRunOrder.AfterPrevious - запуск анимации после окончания предыдущей анимации в рамках группового аниматора. Дополнительные настройки (TfgAnimation.Options)
    TfgAnimationOption.StartFromCurrent - Начинать анимацию с текущего значения свойства компонента, а не указанного стартового значения. TfgAnimationOption.AlwaysSetFinalValue - При остановке анимации, эта опция позволяет указать сразу конечное значение свойства компоненту, иначе конечное значение останется таким, какое оно было на момент остановки. TfgAnimationOption.AutoReverse - Нужно ли автоматически выполнить анимацию в обратном порядке после окончания прямой анимации. Актуально для RepeatCount > 0. TfgAnimationOption.ReleaseControlOnFinish - позволяет автоматически удалить компонент по окончании анимации. TfgAnimationOption.ReleaseAnimationOnFinish - позволяет автоматически удалить аниматор по окончании анимации. Название (TfgAnimation.Name)
    Вы можете присвоить уникальное название аниматору, чтобы в последствии обращаться к нему напрямую по его имени.
    Например:
    var FadeAnimation: TfgAnimation; FadeInAnimation := Button.AnimationManager['fade-in']; Групповые анимации
    Для создания сложных анимаций мы предлагаем использовать групповые аниматоры, задача которых сгруппировать несколько анимаций и задать правила их запуска. По умолчанию групповой аниматор запускает все вложенные анимации одновременно. Например, такой аниматор ниже позволяет реализовать анимацию модального открытия формы на Android:
    var AnimationGroup: TfgAnimationGroup; AnimationGroup := LForm.AnimationManager.AddGroupAnimation .Add(LForm.AnimationManager.CreateScaleAnimation(1.125, 1, 1.125, 1)) .Add(LForm.AnimationManager.CreateOpacityAnimation(0, 1)); Мы создаем два аниматора, объединенные в группу, один из которых масштабирует форму, а второй меняет ее прозрачность.
    Групповой аниматор TfgAnimationGroup по сути так же является аниматором TfgAnimation. А это значит, что вы можете их вкладывать друг в друга добиваясь анимации любой сложности.
    Пример ниже показывает последовательно выполнение анимаций: вначале смещение компонента, а затем поворот:
    Image.AnimationManager.AddGroupAnimation .Add(Image.AnimationManager.AddTranslationAnimation(TPointF.Create(-16, 0), TPointF.Create(16, 0), 1000)) .Add(Image.AnimationManager.AddRotationAnimation(0, 360, 1000) .SetRunOrder(TfgAnimationRunOrder.AfterPrevious)) .Start; group-animation-1.mp4
    Вы можете выделить создание подобной анимации в отдельный метод и затем применять к любому компоненту. 
    Шаблоны анимаций
    Это приятный бонус для наших пользователей. Мы предлагаем базовый набор уже готовых анимаций, которые вы можете использовать в своих проектах всего лишь одной строчкой кода. Для того, чтобы воспользоваться шаблонами, нужно подключить модуль FGX.Animation.Templates.
    Чтобы просто потрясти компонент, достаточно просто написать:
    Image.Shake; А если требуется настроить параметры на свой вкус и цвет, можно воспользоваться перегруженной версией:
    Image.Shake(TfgShakeAnimationParams.Default .SetDuration(2000) .SetRepeatCount(10)); Видео ниже демонстрирует все текущие доступные для использования шаблоны:
    templates.mp4
    Среди шаблонов так же доступны различные варианты появления и скрытия компонентов с переносами и без. Вы можете ознакомиться с текущим набором в демо проекте: "Анимация" -> "Шаблоны анимаций".
    Ограничения
    Несмотря на то, что анимации уже работают, у них есть ряд ограничений, часть из которых мы будем постепенно убирать в будущих версиях.
    Нельзя менять стартовые и конечные значения уже созданных анимаций. Это можно делать только путем удаление старого и создания нового аниматора. Изменение свойств анимации во время ее выполнения не приведет их к применению. Все свойства применяются в момент старта анимации. Изменение размера компонента-контейнера через анимацию не приводит к выравниванию дочерних компонентов. Если вам нужно выполнить выравнивание содержимого, то надо добавлять дополнительные аниматоры для перемещения и изменения размеров вложенных компонентов отдельно.  Устаревшее API
    Из-за создания полноценной анимации и выделения старых хелперов анимации TfgAnimationHelper в шаблоны анимации FGX.Animation.Templates, мы помечаем существующие методы анимации FadeIn, FadeOut, как устаревшие. Через несколько релизов мы удалим эти методы из TfgAnimationHelper, чтобы вы пользовались более удобными средствами анимации.
    На текущий момент методы анимации формы остаются как есть. В ближайших релизах эту анимацию мы встроим прямо в TfgForm, в результате чего анимация открытия/закрытия форм будет осуществляться автоматически и не будет требовать от вас лишних строчек кода.
    Итог
    Мы продолжим развивать и совершенствовать реализацию анимаций. Поэтому нам очень важно получить от вам обратную связь о том, что удобно, что нет и чего не хватает. Эта информация поможет нам сделать анимацию лучше и удобнее для всех.
  4. Yaroslav Brovin
    Всем хорошей рабочей недели!
    Год назад мы поделились руководством по подключению популярного сервиса платформы для аналитики и маркетинга приложения Yandex AppMetrica для Android. В этой статье с мы рассмотрим, как начать использовать этот же сервис для iOS платформы. 
    Подключение FGX Native приложения для Android платформы рассмотрено в другой статье: 
    Для того, чтобы начать использовать AppMetrica в вашем приложении нужно выполнить два шага:
    Зарегистрировать ваше приложение на стороне Yandex Подключить библиотеку Yandex AppMetrika и выполнить инициализацию ключом API.   Регистрация приложения в Yandex AppMetrika
    1. Создаем новый проект в учетной записи AppMetrika и даем ему название:

    Затем далее и добавить. По окончании добавления сервис отобразит ваш API ключ, который нужно запомнить. Он будет использовать для взаимодействия вашего приложения и сервиса Yandex AppMetrika.

    Теперь переходим к настройкам вашего проект, путем нажатия на кнопку настройки проекта в левой панели.

    Теперь нам необходимо связать наш проект с iOS приложением. Среди большого числа настроек нас интересует вкладка "Основное" -> "Universal Link". Нам необходимо заполнить поля Bundle ID и App Prefix на основании данных вашего iOS приложения, зарегистрированного в портале Apple Developer.

    AppPrefix - это уникальный префикс, выданный Apple при участии в программе iOS разработчиков. Он же добавляется к названию пакета iOS приложения, тем самым формируя уникальное название приложения. Его можно найти в личном кабинете Apple на вкладке Membership:
    Bundle ID - это идентификатор пакета вашего FGX Native приложения. Если приложение уже зарегистрировано, то вы можете найти название пакета на сайте Apple в разделе Identifiers. 
    После того, как вы указали все данные, необходимо включить "Использовать Universal Link" - это специальная технология, которая призвана обезопасить передачу статики с устройств пользователя на сервера Yandex.

    Чтобы закончить настройку, необходимо включить поддержку Universal Link на стороне профиля разработчика Apple. Для этого необходимо открыть настройки приложения в консоли Apple и включить поддержку "Associated Domains".

    Теперь ваш профиль приложения сконфигурирован для приема статистики с вашего приложения. Переходим к подключению Yandex AppMetrica к FGX Native приложению. 
    Подключение и инициализация Yandex AppMetrica в FGX Native приложении
    1. Скачиваем статическую версию AppMetrica SDK.
    Yandex SDK 3.17.0.zip
    2. Скачиваем заголовочные файлы для Delphi. 
    Yandex.Api.MobileMetrica.iOS.pas
    3. Скачиваем модуль регистрации и инициализации AppMetrica. Этот модуль содержит код по регистрации вашего приложения в сервисе Yandex для iOS и Android.
    Yandex.AppMetrica.pas
    4. Добавляем модуль в ваш проект и в проектном файле вызываем метод инициализации InitYandexMetrika указанием API ключа, выданном на этапе регистрации проекта в Yandex.
    program YandexMetrika; uses FGX.Application, FGX.Forms, Form.Main in 'Form.Main.pas' {Form.Main: TfgForm}, Yandex.AppMetrica in 'Yandex.AppMetrica.pas'; {$R *.res} begin InitYandexMetrika('Ваш API ключ'); Application.Initialize; Application.CreateForm(TFormMain, FormMain); Application.Run; end. 5. Настраиваем линковку проекта с Yandex AppMetrica SDK.
    5.1. Добавляем в настройках среды переменную окружения "YandexMetrica": "Tools" -> "Options" -> "IDE" -> "Environment Variables". Указываем путь к месту, где у вас распакован AppMetrica SDK.
     
    5.2 В настройках проекта указываем пути для линковки. "Project" -> "Options..." -> "Building" -> "Delphi Compiler" -> "Search Path".
    Выбираем конфигурацию "All Configuration -> iOS Deice 64-bit platform" и указываем следующие пути в "Search Path":
    $(YandexMetrica)\YandexMobileMetrica.framework; $(YandexMetrica)\YandexMobileMetricaCrashes.framework 5.3 В настройках линковки передаем дополнительный ключ "-ObjC" линковки: "Project" -> "Options..." -> "Building" -> "Linking" -> "Options passed to the LD linker".
    6. Все. Теперь выполняем сборку приложения и запуск на устройстве. В результате ваше приложение должно успешно стартовать, а через некоторое время вы увидите статистику в личном кабинете AppMetrica.

     
  5. Yaroslav Brovin
    В следующем релизе FGX Native 1.11.0.0 будет много новых интересных возможностей, которые безусловно найдут свое место в ваших приложениях. В этой статье поговорим о первой новинке: получение фотографии с камеры или из системной галереи изображений.
    До версии 1.11.0.0 получение фотографии с камеры, было не самой тривиальной задачей. Было два решения, которым обычно пользовались разработчики:
    Использование напрямую камеры и разработка своего решение на базе демонстрационного проекта "Компоненты" -> "TfgCamer" -> "Фотокамера". Несмотря на то, что этот способ рабочий и он дает возможности по кастомизации внешнего вида видеоискателя, зачастую требуется просто получить фотографию от пользователя.  Использование нативного API каждой платформы для отправки запроса на получение фотографии системным приложениям. TfgPhotoPicker
    В этом релизе мы предлагаем кроссплатформенное решение в виде специального типа пикера, используемого для выбора фотографии из системной галереи изображений или снятия фотографии с системной камеры. 
    video_2021-05-12_01-16-46.mp4
    Чтобы отправить запрос на получение фотографии с камеры достаточно написать всего три строчки кода:
    uses FGX.Assets, FGX.Assets.Helpers, FGX.Pickers.Photo; procedure TFormMain.btnTakePhotoFromCameraTap(Sender: TObject); begin TfgPickerPhotoFactory.PickPhotoFromCamera(btnTakePhotoFromCamera, procedure(const AFileName: TFileName) begin TfgAssetsManager.Current.AddBitmapFromFile('Photo', AFileName, True); end); end; Для получения фотографии из галереи:
    procedure TFormMain.btnTakePhotoFromLibraryTap(Sender: TObject); begin TfgPickerPhotoFactory.PickPhotoFromLibrary(btnTakePhotoFromLibrary, procedure(const AFileName: TFileName) begin TfgAssetsManager.Current.AddBitmapFromFile('Photo', AFileName, True); end); end; Запрос отправляет через специальную фабрику FGX.Pickers.TfgPickerPhotoFactory, которая предлагает три метода:
    TfgPickerPhotoFactory = class public /// <summary>Создает и возвращает платформенный пикер получения фотографии для ручной работы.</summary> /// <remarks>Удаление пикера лежит на плечах вызываемого кода.</remarks> class function CreatePicker(const AOwner: TObject): TfgPickerPhoto; /// <summary> /// Иницирует получение фотографии с системного приложения камеры. Фотография возвращается в переданную /// анонимную процедуру <c>AOnDidFinishPickingCallback</c>. /// </summary> /// <remarks> /// Вам не требуется следить за временем жизни возвращаемого пикера. Пикер автоматически удалится после того, /// как он будет закрыт пользователем. /// </remarks> class function PickPhotoFromCamera(const AOwner: TObject; const AOnDidFinishPickingCallback: TfgDidFinishPickingCallback): TfgPickerPhoto; /// <summary> /// Иницирует получение фотографии с системного приложения галереи изображений. Фотография возвращается в /// переданную анонимную процедуру <c>AOnDidFinishPickingCallback</c>. /// </summary> /// <remarks> /// Вам не требуется следить за временем жизни возвращаемого пикера. Пикер автоматически удалится после того, /// как он будет закрыт пользователем. /// </remarks> class function PickPhotoFromLibrary(const AOwner: TObject; const AOnDidFinishPickingCallback: TfgDidFinishPickingCallback): TfgPickerPhoto; end; Второй и третий метод предназначены для быстрого получения фотографии с камеры или из галереи. Первый же метод предназначен для ручной настройки пикера и отлова всех событий (открытие, закрытие, отмена и тд.).
    Внимание! Для корректной работы пикера на платформе Android необходимо в настройках проекта включить опцию:
    Project -> Options... -> Application -> Entitlement List -> Security File Sharing = True. Также мы подготовили демонстрационный проект, который позволит вам попробовать эту возможность самостоятельно: "Компоненты" -> "Пикеры" -> "Получение фотографии".
    Всем хорошей рабочей недели. Следите за будущими анонсами FGX Native 1.11.0.0.
  6. Yaroslav Brovin
    В этот вторник совместно с компанией Embarcadero проводим вебинар посвященный разработке мобильного приложения с использованием FGX Native. На вебинаре разберем приемы верстки экранов мобильного приложения и одну из возможных примеров системы навигации между экранами.
    В качестве примера создаем прототип мобильного приложения, позволяющего выработать полезные привычки.
    Когда: 31 мая 2022 в 12:00 МСК Язык: русский Заголовок: Создание популярного мобильного приложения с FGX Native Ссылка на регистрацию: https://lp.embarcadero.com/fgx-native  
  7. Yaroslav Brovin
    Добрый день, уважаемые разработчики.
    Спешим порадовать вас главным нововведением 1.15.5.0 - это поддержка векторных изображений SVG.
    Добавление и использование
    В новой версии добавлен новый тип ресурсов - SVG изображение. В него вы можете загрузить SVG файл в дизайнере ресурсов для дальнейшего использования в компонентах. Добавление SVG изображения доступно в дизайнере ресурсов: Project -> FGX Assets Manager.
     
    При использовании данного изображения в компонентах, происходит автоматическое формирование растрового изображения TfgBitmap с требуемым текущим коэффициентом масштабирования окна и указанным размером иконки. При этом логический размер (dp, без учета коэффициента масштабирования) иконки указывается в окне редактирования SVG файла.

    Особенности реализации
    Как выяснилось в ходе разработки, iOS и Android не поддерживают SVG изображения. Вместо этого, они конвертируют SVG файлы в Android Studio / XCode в свой внутренний промежуточный формат, которыйв свою очередь уже используется для формирования растровых изображений. 
    В связи с этим, нам пришлось полностью своими силами разработать парсер и рендер SVG. В итоге мы выполняем обработку SVG файла на стороне Delphi и формируем наш формат, используемый для отрисовки SVG. Далее в рантайме по файлам нашего формата, генерируем изображения.
    Генерация 
    При необходимости вы можете сформировать экземпляр растрового изображения TfgBitmap по SVG изображению любого размера. Для этого вам нужно запросить ресурс у TfgAssetsManager. При этом вы отвечаете за удаление экземпляра TfgBitmap:
    uses FGX.Canvas, FGX.Canvas.Types, FGX.Assets, FGX.Assets.SVG; var Asset: TfgAssetSVG; Bitmap: TfgBitmap; begin if TfgAssetsManager.Current.Find<TfgAssetSVG>('SVG asset name', Asset) then begin Bitmap := Asset.CreateBitmap(TSizeF.Create(100, 20)); try // Works with bitmap. finally Bitmap.Free; end; end; end; Ограничения
    1. Неполноценная поддержка спецификации SVG. Мы проверили наш SVG парсер на иконках разных поставщиков от MaterialDesign, Microsoft Visual Studio Icons и до Icons 8. Однако, стандарт SVG довольно большой и написать за такое короткое время полноценный парсер задача довольно непростая. Несмотря на это, мы хотели бы, чтобы вы делились с нами SVG файлами, которые не могут прочитаться и отобразиться корректно. Это поможет нам оперативнее расширять парсер в тех направлениях, которые нужны для отображения массовых SVG изображений. 
    При загрузке изображения в дизайнере, у иконки появится специальный значок ошибки, если она не поддерживается.
     
    Если файл загрузился и может быть отображен, но при чтении не удалось распознать какие-либо элементы, то он помечается значком-предупреждением:

    Это означает, что теоретически изображение может отобразиться, но оно может отображаться не корректно.
    2. Отображение превью svg иконки в дизайнере ассетов не говорит о поддержке SVG файла. Для отображения SVG изображений в IDE мы используем Skia4Delphi. Однако, практика показала, что иногда SVG иконки, которые мы отображаем успешно на устройствах, не отображаются корректно в Skia4Delphi.
    Например, отображение иконки в Runtime через FGX Native:

    В то же время, Skia4Delphi (дизайнер IDE) просто показывает черный квадрат:

    Поэтому вам необходимо временно проверять корректно отображения SVG иконки именно на основании Runtime. Несмотря на то, что мы можем формировать SVG изображения и в дизайнере тоже нашим средство, мы пока используем Skia4Delphi, чтобы была возможность сравнить, как иконки должны отображаться корректно (парсер SVG должен по идее поддерживать больше функций). Спустя время, как только наш парсер будет поддерживать все ваши иконки, мы заменим в дизайнере рендер SVG иконок в дизайнере на наш.
    3. Временно недоступна возможность динамически добавлять SVG файл в рантайме. Например, вы загрузили SVG файл в рантайме и хотите его отобразить. Несмотря на то, что технически у нас уже есть такая возможность, наш парсер пока не оптимизирован, чтобы выполнять преобразование SVG формата достаточно быстро. Чтобы потенциально не побуждать пользователей к проседанию производительности, данная возможность временно недоступна. При этом мы считаем, что такой сценарий довольно редкий. И по большей части, вы будете использовать именно заранее добавленные SVG изображения.   
  8. Yaroslav Brovin
    В ближайшие дни выйдет новый релиз библиотеки FGX Native 1.9.3.0, главной новинкой которой будет утилита командной строки для генерации заголовочных файлов Delphi для использования Java API /Android API. 
    Утилита располагается в каталоге "$(FGX)\Tools\Java2Delphi" и в ее состав входят:
    Java2Delphi.exe - утилита. config.template.json - Шаблон конфигурационного файла, в котором указаны правила формирования итоговых Delphi-модулей. api-versions.android-28.xml - Файл с информацией об Android API 28 версии. UsedGUID.txt - набор использованных GUID значений для Delphi интерфейсов. run.bat - пакетный файл для запуска утилиты в любом месте. Перед первым применением необходимо выполнить настройку утилиты.
    Настройка
    Управление утилитой идет при помощи конфигурационного json-файла, передаваемого через аргументы командной строки. Перед первым применением, необходимо:
    1. Создать каталог в любом месте, где будут размещены все результаты генерации.
    2. Скопировать файл "run.bat" в каталог (1).
    3. Скопировать конфигурационный шаблон "config.template.json" в каталог (1) и назвать его "config.json" (название может быть любым).
    4. Отредактировать базовые настройки Java окружения:
    "javap": "$(JAVA_HOME)\\bin\\javap.exe", "jar": "$(JAVA_HOME)\\bin\\jar.exe", Java2Delphi использует две утилиты командной строки из Java SDK: "javap.exe" и "jar.exe". По умолчанию она использует переменную окружения $(JAVA_HOME). Если она у вас указана в Windows, то этот шаг можно пропустить. Если нет, то либо необходимо добавить ее с указанием местоположения Java JDK:

    или напрямую указать пути к требуемым утилитам.
    5. Указываем пути, куда будут помещены результаты генерации (опционально).
    "outputDir": ".\\Headers", "signatureOutputDir": ".\\Signatures", outputDir - каталог, куда будут помещены pas файлы с необходимыми обертками Java/Android Api. signatureOutputDir - вспомогательный каталог, куда будут помещены вспомогательные файлы с сигнатурами Java классов. После выполнения первичной настройки файла конфигурации, приступим теперь к указанию, какие Java классы надо транслировать.
    Правила генерации файлов
    Для того, чтобы сгенерировать заголовочные файлы необходимо указать:
    Jar-библиотеки, которые содержат необходимые Java классы. Указать требуемые для перевода Java классы. Указать, в какие Delphi модули разместить результирующие обертки. 1. Указываем нужные jar библиотеки.
    Библиотеки указываются в секции jars:
    "jars": [ "E:\\Development\\Android\\platforms\\android-28\\android.jar" ], По умолчанию, мы всегда подключаем jar библиотеку Android (по-этому не удаляйте ее, если хотите совместимости с Android API).
    Вам необходимо откорректировать расположение данного файла с учетом вашей системы. 
    2. Указываем требуемые для перевода Java классы.
    Теперь мы должны сказать утилите в секции "classes", какие классы нас интересуют в этих библиотеках. Здесь необходимо использовать полное название Java типов с учетом названия пакета:
    "classes": [ "java.lang.Object", "java.lang.CharSequence", "java.lang.String", "java.lang.Class", "java.lang.Boolean", "java.lang.Number", // Можно использовать * на конце пакета, чтобы выбрать все классы в данном пакете "android.provider.*", "android.view.animation.Animation", "android.view.animation.Animation.AnimationListener", "android.view.animation.Animation.Description", // и тд По умолчанию шаблон включает в себя все классы из Android API, которые используются в FGX Native. Поэтому не удаляйте эти классы, если вы хотите получить заголовочные файлы, совместимые с уже существующими хедерами в FGX Native.
    Добавьте сюда те, которые вас интересуют.
    Поскольку в некоторых библиотеках могут быть 1000 классов. То обработка такого количества классов займет много времени. Помимо этого, вы получите гигантских размеров заголовочные файлы. Поэтому лучше добавлять только те классы, которые вам реально нужны для вызовов, а не все.
    3. Указываем правила генерации Delphi модулей.
    После того, как мы перечислили интересуемые нас Java классы, нам остается только указать, в какие Delphi модули их следует поместить. Это делается через "output":
    "output": [ { "name": "Android.Api.Text", // Название Делфи модуля Android.Api.Text.pas "uses": [ // Подключаемые Делфи модули в Android.Api.Text.pas "Android.Api.JavaTypes", "Android.Api.ActivityAndView" ], "classes": [ // Java классы, обертки над которыми будут добавлены в Android.Api.Text.pas "android.text.*" ], "exclude": [ // Опционально, можно исключить некоторые классы "android.text.method.*", "android.text.util.*", "android.text.ClipboardManager*" ] } ] Генерация
    Поздравляю, теперь вы можете запустить утилиту и наблюдать, как она трудится формируя заголовочные файлы по вашим правилам.

    Обратите внимание, что первый запуск занимает много времени, так как утилита генерирует сигнатуры Java классов (именно они используются для анализа структуры Java классов) и помещает их в каталог "signatureOutputDir". Однако, последующие запуски будут выполняться моментально, за счет того, что утилита не выполняет повторную генерацию сигнатур, если она уже есть. 
    Не удаляйте файлы сигнатур, они помогут ускорить повторную генерацию заголовочных файлов.
    Разбор пропущенных классы/методов/полей
    В процессе генерации утилита выполняет много сложной работы с анализом зависимостей, доступных типов и тд. Если какой-то из Java типов не указан в секции "classes", то утилита пропустит все его использования. При этом она сообщит об этом через лог.
    Например: При генерации класса android.text.HTML были пропущены два метода fromHTML (они не попали в pas файлы), потому что не был указан тип android.text.Html$ImageGetter.
    Skipped members of the Java type: android.text.Html Methods: - fromHtml(const AArg0: JString; const AArg1: Integer; const AArg2: JHtml_ImageGetter; const AArg3: JHtml_TagHandler): JSpanned; [android.text.Html$ImageGetter] - fromHtml(const AArg0: JString; const AArg1: JHtml_ImageGetter; const AArg2: JHtml_TagHandler): JSpanned; [android.text.Html$ImageGetter] Если вам нужны эти методы, то необходимо в секцию classes добавить этот тип:
    "classes": [ "android.text.Html.ImageGetter", Обратите внимание, что в Java "$" - это разделитель для вложенных классов. Однако в конфигурационном файле вместо него надо использовать ".".
  9. Yaroslav Brovin

    Общая
    Поздравляем вас с наступающим Новым годом!
    Мы благодарим вас за доверие и спасибо, что в этом году вы были с нами. В течении всего года мы прислушивались к вашим пожеланиям и старались сделать все возможное, чтобы наш продукт раз от раза становился удобнее и мощнее.
    В ближайшее время выйдет новый релиз 1.13.3.0. Традиционно в этом релизе вас ждет несколько полезных новинок и улучшений.
    GooglePay - TfgGooglePayClient 
    Первая новинка - это новый компонент TfgGooglePayClient оплаты покупок в вашем приложении через платежную систему GooglePay.
    Настройка 
    Для того, чтобы добавить возможность оплаты покупок в вашем приложении, вам необходимо выполнить первичную настройку на стороне Google - https://developers.google.com/pay/api/web/guides/setup?hl=ru
    В результате этой настройки вы получите идентификатор продавца Google - MerchantId.
    Настройка приложения FGX Native
    Для того, чтобы иметь возможность использовать GooglePay в FGX Native приложении, необходимо добавить в манифест Android приложения следующие строчки в любое место внутрь тега <application>:
     1. Версию GooglePlay сервисов. ТОЛЬКО, если вы не используете "Firebase PushNotification", Maps или AdMob.
    <meta-data android:name="com.google.android.gms.version" android:value="12451000" /> 2. Включить доступ приложения к кошельку:
    <meta-data android:name="com.google.android.gms.wallet.api.enabled" android:value="true" /> 3. Указать идентификатор продавца в свойстве "TfgGooglePayClient.MerchantName", полученный на предыдущем шаге.
    Проверка возможности оплаты через GooglePay
    GooglePay может быть недоступен на устройстве пользователя, поэтому, перед инициированием процесса оплаты, необходимо узнать текущий статус. Это можно сделать через асинхронный метод "TfgGooglePayClient.IsReadyToPayAsync":
    uses FGX.Payments.GooglePay.Types; if gpClient.IsSupported then gpClient.IsReadyToPayAsync(False, procedure (const AStatus: TfgGooglePayStatus) begin // Отображаем/Скрываем кнопку оплаты через GooglePay btnPay.Visible := AStatus = TfgGooglePayStatus.Available; end); Оплата через GooglePay
    Для инициации оплаты необходимо заполнить информацию о проводимой транзакции и воспользоваться асинхронным методом оплаты "TfgGooglePayClient.PayAsync". Минимально необходимая информация в транзакции - это код валюты и цена.
    procedure TFormMain.btnPayTap(Sender: TObject); var TransactionInfo: TfgTransactionInfo; begin TransactionInfo := TfgTransactionInfo.Create; try { Информация о покупке } TransactionInfo.SetCurrencyCode('RUB') .SetTotalPrice(100.12); gpClient.PayAsync(TransactionInfo); finally TransactionInfo.Free; end; end; Описание полей транзакции, смотрите в исходном коде или на сайте GooglePay.
    По результатам обработки запроса, будет вызвано одно из событий:
    "OnPaymentError" - в ходе оплаты произошла ошибка. "OnPaymentComplete" - пользователь выполнил оплату, токен для проведения оплаты передается в параметрах события. "OnPaymentCancelled" - пользователь отменил оплату. Если платеж прошел успешно, то в событии OnPaymentComplete будет передан JSON ответ, из которого вы можете извлечь необходимую информацию согласно документации:
    procedure TFormMain.gpClientPaymentComplete(Sender: TObject; const AResponse: TJSONValue); begin mLog.Lines.Add(Format('Completed: AResponse="%s"', [AResponse.Format])); end; После извлечения платежного токена, вам необходимо провести транзакцию на вашем сервере через ваш банк.
    Если же оплата по каким-то причинам не прошла, вы можете получить информацию об ошибке через событие OnPaymentError. В параметрах передается код ошибки и ее описание. Обратите внимание, что данный код и описание выдается самой системой. Поэтому иногда описание может отсутствовать или содержать неполную информацию (это нормально).
    Диагностика и отладка
    Все детальные ошибки настройки интеграции с GooglePay обычно выводятся в лог. Именно там можно узнать, почему платеж не запускается или не проходит. Обратите внимание, что чаще всего ошибки в лог попадают не от имени приложения, а от системы. Поэтому при поиске проблем, нужно это учитывать (не фильтровать сообщения вашим приложением).
    Все свойства компонента полностью соответствуют оберткам в документации Google Pay. Компонент формирует JSON запрос, который отправляется в нативное API. Вы можете проверить корректность формирования JSON запроса через лог. Для этого необходимо включить расширенное логирование в вашем приложении:
    TfgLog.MinimumLevel := TfgLogLevel.Trace; Полезные ссылки:
    Руководство по использованию бренда GooglePay. Советы UX по интеграции GooglePay. Чек лист по настройке GooglePay. Диагностика проблем. TfgApplicationEvents
    При разработке Android приложений и при использовании Android API очень часто требуется отправить запрос другим активностям и получить от них результат. Раньше это решалось через подписку на широковещательные сообщения  FGX.Platform.Android.TfgActivityResultMessage и TfgActivityNewIntentMessage и требовало написать следующий код:
    constructor TFormMain.Create(AOwner: TComponent); begin inherited; TMessageManager.DefaultManager.SubscribeToMessage(TfgActivityResultMessage, ActivityResultHandler); TMessageManager.DefaultManager.SubscribeToMessage(TfgActivityNewIntentMessage, ActivityNewIntentHandler); end; destructor TFormMain.Destroy; begin TMessageManager.DefaultManager.Unsubscribe(TfgActivityNewIntentMessage, ActivityNewIntentHandler); TMessageManager.DefaultManager.Unsubscribe(TfgActivityResultMessage, ActivityResultHandler); inherited; end; procedure TFormMain.ActivityNewIntentHandler(const Sender: TObject; const M: TMessage); var Message: TfgActivityNewIntentMessage; begin TfgAssert.IsClass(M, TfgActivityNewIntentMessage); Message := TfgActivityNewIntentMessage(M); TfgToast.Show(Format('Получен новый интент: intent=%s', [JStringToString(Message.Value.toString)])); end; procedure TFormMain.ActivityResultHandler(const Sender: TObject; const M: TMessage); var Message: TfgActivityResultMessage; begin TfgAssert.IsClass(M, TfgActivityResultMessage); Message := TfgActivityResultMessage(M); // Intent is available in Message.Data TfgToast.Show(Format('Получен результат из другого приложения: requestCode=%d, resultCode=%d', [Message.RequestCode, Message.ResultCode])); end; Сейчас же мы добавили два новых события "TfgApplicationEvents.OnActivityResult" и "TfgApplicationEvents.OnActivityNewIntent", которые позволяют значительно сократить код и сконцентрироваться только на реализации. 
    procedure TFormMain.fgApplicationEvents1ActivityNewIntent(Sender: TObject; const AIntent: TfgAndroidIntent); begin {$IFDEF ANDROID} TfgToast.Show(Format('Получен новый интент: intent=%s', [JStringToString(AIntent.toString)])); {$ENDIF} end; procedure TFormMain.fgApplicationEvents1ActivityResult(Sender: TObject; const ARequestCode, AResultCode: Integer; const AIntent: TfgAndroidIntent); begin {$IFDEF ANDROID} TfgToast.Show(Format('Получен результат из другого приложения: requestCode=%d, resultCode=%d', [ARequestCode, AResultCode])); {$ENDIF} end; TfgCollectionView
    Мы провели улучшение контроллера выделения элементов и предоставили больше гибкости в точной настройке поведения. Раньше при режиме выделения  "TfgCollectionViewSelectionMode = SingleSelect" мы не позволяли сбросить выделение элемента повторным нажатием. Это было сделано для возможности реализации меню с отображением текущего выбранного раздела. Однако, для некоторых пользователей такое поведение компонента было нежелательным.
    Теперь мы предлагаем вам самостоятельно решить, как должно работать выделение при помощи новых событий  "TfgCollectionView.OnItemCanSelect" и "TfgCollectionView.OnItemCanDeselect". Данные события вызываются непосредственно до момента установки или снятия выделения и позволяют отменить действие при помощи передаваемого параметра AAllow.
    Так же мы исправили ряд ошибок выделения на платформе iOS, так что теперь процесс выделения работает одинаково на всех платформах.
    ВАЖНО! Теперь по умолчанию компонент позволяет сбросить выделение элемента в режиме SingleSelect. 
    Заключении
    Желаем вам свершения всего задуманного, интересных проектов и амбициозных целей!
    Спасибо, что вы с нами!
  10. Yaroslav Brovin
    Рады поделиться новинками будущего релиза 1.12.0.0. 
    Очень часто при разработке мобильных приложений разработчики сталкиваются с одними и теми же задачами. Большинство решений, которые есть в интернете, ориентированы под FMX. Однако, зачастую из-за зависимостей в RTL на FMX Java код, эти решения нельзя просто добавить в FGX Native как есть и использовать.
    Поэтому мы решили по мере возможностей предлагать вам решения этих задач в виде расширений для FGX Native. Расширение будет включать необходимую обвязку/компоненты для FGX Native для сторонних OpenSource проектов. Первым таким расширением будет реализация нового компонента генерации штрихкода TfgBarcode на основе библиотеки Zint (https://www.zint.org.uk/).
    Отображение штрихкодов
    Новый пакет расширения FMXNative.Extension.Zint включен в поставку 1.11.8.0. Пакет содержит новый компонент TfgBarcode, поддерживающий отображение огромного числа типов штрихкодов. 

    Свойство Symbology позволяет выбрать тип отображаемого штрихкода. Через свойство Data указываются кодируемые данные. А через группу свойств Appearance вы можете настроить визуальное отображение штрихкода.
    Новое правило именования пакетов FGX Native
    Мы пересмотрели правила именований пакетов библиотеки FGX Native. Поэтому, если вы создаете свои пакеты расширений на основе FGX Native, вам необходимо переименовать зависимости на пакеты.
    FGXNative.Core <- fgx FGXNative.Design <- fgx_design FGXNative.Registration <-fgx_reg FGXNative.Extension.Translator <- FGXTranslatorD FGXNative.Externals <- fgx_externals Так же, если вы используется наш компонент локализации, необходимо:
    После установки новой версии библиотеки запустить среду Среда отобразит ошибку при загрузке пакета FGXTranslatorD. Необходимо отвечать Yes во всех диалогах про этот пакет. Затем открыть список компонентов среды: "Components" -> "Install Packages..." В списке найти строчку, соответствующую FGXTranslatorD и удалить ее при помощи кнопки "Remove". Данный компонент мы теперь регистрируем автоматически при установке FGX Native. Календарь под iOS
    В новой версии так же появится реализация компонента TfgCalendar для iOS. iOS не имеет родной реализации календаря. Поэтому мы с нуля реализуем данный компонент.
    Заключение
    Если у вас есть потребность в миграции некоторых невизуальных компонентов из FMX, пожалуйста, отпишитесь здесь в комментариях:
    Ссылку на стороннюю библиотеку с наличием исходных кодов/OpenSource. Без доступа к исходным кодам мы не сможем выполнить адаптацию под FGX Native. Описание для чего данный компонент нужен. Если кто-то из участников уже озвучил в комментариях нужный вам компонент, просьба отметить такое сообщение реакцией (лайком).
  11. Yaroslav Brovin

    Общая
    Вот и прошло лето, а в месте с тем пришло время для крупного релиза, с которым мы вас с удовольствием познакомим.
    FlexBox дизайнер

    Система разметки FlexBox - универсальный инструмент, предлагающий довольно большой ряд редактируемых параметров, разобраться с которыми при первом приближении довольно сложно. В этом релизе мы разработали визуальный редактор параметров для контейнеров FlexBox, наглядно отображающий разные режимы выравнивания. Редактор доступен у свойства TfgLayout.AlignmentChildren, достаточно нажать на кнопку "..." у значения свойства AlignmentChildren любого компонента-контейнера. Он не просто показывает работу опций, но так же и адаптирует их при смене ориентации и тд.
    FlexBoxDesigner.mp4.fa3fd3bd9129367f28613c726623cc0b.mp4
    Переработанный дизайнер ресурсов
    В этом релизе мы полностью переработали дизайнер ресурсов, как внутри, так и снаружи. Добавили новые возможности и улучшили старые. Но обо всем по порядку.
    Автоматическое удаление "лишних" ресурсов
    В старой версии дизайнера при добавлении ресурса цвета создавалось 5 одинаковых ресурсов цвета для каждого типа устройств (Universal, Phone, NormalTablet, LargeTablet, ExtraLargeTablet). Однако, как показала практика,  пользователи используют на всех устройствах один и тот же цвет. Поэтому генерация одинаковых цветов нецелесообразна и с точки зрения дублирования, и с точки зрения скорости загрузки приложения и поиска ресурсов. В связи с этим, теперь дизайнер автоматически определяет дубликаты и удаляет их.
    Было:

    Стало:

    Генерация изображений
    Часто приложение начинает создаваться без наличия готового дизайна. Это в свою очередь усложняет разработку макета, так как зачастую заказчик визуально оценивает прототип. А для этого требуется искать подходящие графические изображения-заглушки, для использования в UI, чтобы разметка смотрелась законченно.
    Теперь можно сгенерировать изображение-заглушку по вашим параметрам и использовать его при проектировании приложения.


    Яркость изображения
    Улучшен алгоритм определения яркости изображений. Теперь более корректно определяется контрастный цвет фона для предварительного просмотра. Напомним, если иконка/изображение использует белые оттенки цветов (светлая), то для предварительного просмотра подбирается темный фон и наоборот.


    Импорт изображений
    Теперь можно перетащить папку с изображениями на дерево ресурсов и импортировать все вложенные изображения.
    Стандартизация редактирования ресурсов.
    Теперь все ресурсы выглядят в дизайнере одинаково, а все операции редактирования перенесены в выпадающее окно, которое открывается при нажатии на предварительное отображение ресурса.


    Умное отображение цвета
    Если ресурс цвета содержит известный код цвета, то вместо кода отображается название цвета. При наведении курсора мыши на превью цвета, отображается ARGB раскладка цвета.

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

    Улучшения в TfgButton
    Добавлено верхнее и нижнее положение иконки относительно текста. Теперь можно делать кнопки для нижней панели навигации без дополнительных комбинация стандартных компонентов.

    20220904_174816.mp4
    Многопоточный режим TfgPickerPhoto
    При использование в пикере выбора фотографий устройства множественного выбора, процесс извлечения фотографий в вашем приложении мог занимать много времени (в зависимости от количества изображений и их размера). При этом приложение зависало до конца обработки всех фотографий. В этом релизе мы добавили многопоточную загрузки изображений и ускорили их обработку. Чтобы пользователь понимал, что процесс может занимать длительное время, мы добавили анонимную функцию для возможности контроля процесса обработки.
    TfgPickerPhotoFactory.PickPhotosFromLibrary(btnTakePhotoFromLibrary, procedure(const AFileNames: TfgPhotoFiles) begin LoadImages(AFileNames); end, procedure(const AProgress: Single) begin ShowProgress(AProgress); end); Поддержка Android 12
    Добавлена поддержка Android 12. Для корректной публикации приложений в "Google Play Market", необходимо обновить манифест Android приложения. При открытии старого приложения, IDE предложит обновить манифест автоматически. Если вы вносили ручные правки в шаблон манифеста приложения, то перед обновлением манифеста сделайте его копию и затем заново внесите ваши правки. 
    Если по каким-то причинам среда не предложила обновить манифест (если вы ранее отказались от обновления шаблона), то вы можете это сделать вручную. Для этого необходимо добавить атрибут `android:exported="true"` в узел `activity`.

    Delphi 11 Update 2
    Добавлена поддержка Delphi 11 Update 2. Из-за критических изменений на стороне IDE, перестали собираться приложения FGX Native из-за нарушения целостности в Deployment Manager. Данный релиз устраняет эту проблему.
    Надеемся, что новый функционал придется вам по душе и облегчит процесс разработки. Через релиз мы выпустим поддержку стилей, которые значительно упростят процесс верстки интерфейса вашего приложения в дизайнере.
  12. Yaroslav Brovin
    Добрый день, уважаемые разработчики.
    В этой статье мы рассмотрим настройку и использование сервиса Firebase Messaging (FCM)для приема push-уведомлений на платформе iOS.
    Начиная с версии FGX Native 1.15.2.0, вы можете использовать Firebase не только на Android, но теперь и на iOS. Тем самым не нужно выполнять дополнительные действия для конвертации токенов устройств из Apple Push Service (APS) в токен Firebase (FCM), сделав ваш код полностью кроссплатформенным.
    Перед тем, как приступить к работе с уведомлениями, необходимо выполнить регистрацию аккаунта Google.
    Регистрация приложения в Firebase для iOS
    1. Откройте Firebase Console и выполните вход в учетную запись Google. 
    2. Нажмите кнопку "Создать проект"

    3. Укажите название вашего проекта и нажмите кнопку "Continue". Вы можете использовать любое название. Например: "FGX Native project". 

    4. На текущей странице вы можете отключить поддержку Google Analytics. Вы можете ее отключить. Однако, рекомендуем ее оставить. Так в будущем вы сможете использовать аналитику для данного проекта. Нажмите кнопку "Continue".

    5. Выберите или создайте аккаунт Google Analytics. Я взял созданные раннее, в который будет добавлена аналитика для нового проекта. Нажмите кнопку "Create Project".

    6. Ожидаем окончания процесса создания проекта в Firebase. По окончании нажимаем "Continue". Когда проект будет создан, вы увидите главную страницу:

    7. Теперь необходимо зарегистрировать ваше FGX Native iOS приложение для этого нажимаете на кнопку с логотипом iOS.
    8. Указываете имя пакета вашего iOS приложения, такое же, как у вашего приложения (если оно есть), или такое, какое оно будет (его можно будет указать)
    Если у вас уже есть приложение, то узнать название пакета можно в настройках проекта: Project -> Options... -> Version Info >  CFBundleIdentifier ". Если у вас еще нет приложения, то придумайте название пакета. Например: "ru.fgx.pushnotificationdemo".
    9. Нажмите кнопку "Register app".
    10. Скачиваем конфигурационный файл "GoogleService-Info.plist", нажав на кнопку "Download" и сохраняем файл в папку FGX Native проекта (можно сохранить в любое другое место). Этот файл содержит настройки для инициализации FGX Native приложения на iOS.
    14. Заканчиваем добавление вашего приложения путем нажатия кнопок "Next" -> "Next" -> "Continue to console"
    15. Теперь вы увидите ваше iOS приложение на главной странице.

    Теперь необходимо выполнить настройку FGX Native приложения в IDE.
    Настройка приложения
    1. Откройте существующий проект или создайте новый.
    Если вы создаете новый проект, то в мастере создания проекта обязательно укажите (на основании шага 8 регистрации приложения в Firebase): Идентификатор организации. Например: "ru.fgx". Имя проекта. Например: "pushnotificationdemo". Если открываете существующий, то убедитесь, что название пакета в Project -> Options... -> Version Info >  CFBundleIdentifier " соответствует названию из шага 8. 2. Откройте: "Главное меню" -> "Project ->  Deployment".
    2.1. В выпадающем списке конфигурации выберите "All Configurations -> iOS-Device 64-bit platform".
    Примечание: Если вы хотите разделять настройки для разных конфигураций, выберете соответствующую конфигурацию.    
    2.2. Добавьте ранее скаченный файл настроек "GoogleService-Info.plist" (на основании шага 10 регистрации приложения в Firebase).

    Примечание: Если этого не сделать, то при старте приложение упадет.
    3. Откройте настройки проекта: "Project -> Options... -> Application > Services".
    4. Перейдите к странице "Application -> Entitlment list". И поставьте галку напротив: "Receive push notification". 
    3. Сохраните изменения.
    4. Откройте главную форму вашего приложения и подключите модуль FGX.PushNotification.FCM. 
    Примечание: FGX Native использует Firebase по умолчанию на Android. Однако, на iOS мы используем Apple Push Service по умолчанию. Поэтому Firebase библиотека не линкуется по умолчанию. По этому важно подключить данный модуль. 
    5. Добавьте на форму компонент TfgPushNotificationService и укажите "ServiceNamne = FCM".
    Минимально необходимая настройка вашего приложения для получения пуш уведомлений выполнена. Если приложение будет свернуто или выгружено из памяти, то при отправке push-уведомление отобразиться в центре уведомлений устройства. Если же приложение активно, то пуш уведомление не будет попадать в центр уведомления устройства.
    Получение токена устройства
    При первом старте приложения, оно выполняет свою регистрацию в облаке Firebase. При этом облако выдает устройству уникальный токен DeviceToken. Перед тем, как отправить тестовое уведомление, необходимо узнать токен устройства. Для получения токена устройства нужно написать обработчик события "TfgPushNotificationService.OnDeviceTokenChanged". Данное событие вызывается каждый раз, когда выдается новый токен устройства. Токен устройства может меняться.
    Получив токен, вам необходимо передать его на ваш сервер, отправляющий Push-уведомления для будущих отправок сообщений. 
    В данном примере, мы не будем его сохранять, а выведем в лог устройства.
    procedure TFormMain.PushServiceDeviceTokenChanged(Sender: TObject; const ADeviceToken: string); begin TfgLog.Info('Device token changed: deviceToken="%s"', [ADeviceToken]); end; Получение push-уведомления
    Для получения токена устройства нужно написать обработчик события "TfgPushNotificationService.OnPushNotificationReceived". Данное событие вызывается, когда устройство принимает push-уведомление.
    Примечание: В зависимости от реализации, событие может вызываться в разные моменты времени. Например, если приложение не в памяти или в фоне, то событие вызовется в момент нажатия пользователем на уведомление в Центре локальных уведомлений.
    Пример вывода в лог полученного push-уведомления:
    procedure TFormMain.PushServicePushNotificationReceived(Sender: TObject; const ANotification: TfgPushNotification); begin TfgLog.Info('Notification received: notification="%s"', [ANotification.ToString]); end; Запустите приложение и запомните токен устройства из консоли. 
    О том, как пользоваться Console для просмотра логов устройства, читайте в статье:
    Отправка тестового сообщения
    Чтобы отправить тестовое сообщение на конкретное устройство, можно воспользоваться Firebase консолью.  
    1. Откройте консоль: https://console.firebase.google.com
    2. Перейдите в меню навигации к разделу: "Engage -> Messaging"
    3. Нажмите кнопку "Create your first campaign".
    4. Выберите тип "Firebase Notification Messages".
    4. Заполните данные уведомления. Например так:

    5. Нажмите кнопку "Send test message". Консоль предложит указать на какие устройства необходимо отправить данное уведомление.
    6. Укажите в поле токен устройства (раздел "Получение токена устройства") и нажмите кнопку "+".
    7. Выполните отправку уведомления, путем нажатия на кнопку "Протестировать" и наблюдайте появление вашего уведомления в центре локального уведомления на устройстве:

    Получение данных уведомления при старте приложения
    Если пользователь нажимает на push-уведомление в центре уведомлений, то система запустит ваше приложение. В любой момент времени можно получить данные стартового уведомления напрямую из локального Firebase сервиса (Раздел "Получение Push-уведомлений").
    var Notifications: TArray<TfgPushNotification>; begin Notifications := fgPushNotificationService1.StartupNotifications; end;
  13. Yaroslav Brovin
    Добрый день, уважаемые разработчики. 
    Скоро выйдет новое крупное обновление 1.16.0.0, которое содержит ряд новых компонентов, серию улучшений и исправление ошибок. Одним из новых компонентов станет интерактивный выбор прямоугольной области, который может использоваться для обрезания изображения. 
    Компонент представляет собой слой, который можно наложить поверх редактируемого объекта. Например, TfgImage.  Для удобства расположения, можно воспользоваться якорями FlexBox - свойство RelativePosition.

    При помощи точек выделения можно интерактивным путем расширить регион. А получить или указать его координаты можно при помощи свойства TfgRegionPicker.Region. При интерактивном изменении области вызывается событие OnChanged. 
    При первом отображении компонента можно отцентрировать регион при помощи свойства RegionOptions.DefaultPlacement. 
    Настройки внешнего вида
    Компонент предлагает набор настроек, позволяющие настроить отображение отдельных элементов компонента, таких как:
    Точки выделения (Appearance.Grip). Здесь можно поменять размер точек и параметры кистей заливки и контура.
    Регион (Appearance.Region). Здесь можно поменять параметры отрисовки пунктирной рамки области выделения. Затемнение вне региона (ContentShading). По умолчанию все, что вне области выделения затемняется. Это настраивается этим свойством. Можно указать базовый цвет затемнения и ее прозрачность.
    Кадрирование
    В релизе 1.16.0.0 добавлены новые метод, позволяющие легко выполнить базовые преобразования. К одному из таких методов относится кадрирование битмапа по области TfgBitmap.CreateCropped (Чтобы воспользоваться методом нужно подключить модуль FGX.Bitmap.Helpers). Ниже представлен пример кода, выполняющий кадрирование изображения на основании координат региона, полученных от TfgRegionPicker. Пример доступен в разделе "Компоненты" -> "TfgRegionPicker" -> "Обрезание фотографии":
    procedure TFormMain.btnCropTap(Sender: TObject); var Region: TRectF; AssetBitmap: TfgAssetBitmapSet; Bitmap: TfgBitmap; CroppedBitmap: TfgBitmap; CroppedRect: TRectF; Scale: Single; DestRect: TRectF; begin Region := rpCropRegion.Region.ToRectF; if TfgAssetsManager.Current.Find<TfgAssetBitmapSet>(R.Bitmap.PHOTO, AssetBitmap) and AssetBitmap.FindBitmap(Bitmap) then begin // Вычисляем область вывода битмапа в контроле. DestRect := TRectF.Create(FBitmap.Bounds).FitInto(pbOriginal.Bounds, Scale); // Корректируем положение региона. Region.Offset(-DestRect.TopLeft); // Масштабируем область согласно физическому размеру битмапа. CroppedRect.TopLeft := Region.TopLeft * Scale; CroppedRect.BottomRight := Region.BottomRight * Scale; // Кадрируем исходный битмап CroppedBitmap := Bitmap.CreateCropped(CroppedRect.Truncate); TfgAssetsManager.Current.AddBitmap('Cropped Image', CroppedBitmap); end; end; Результат работы представлен на видео ниже:
    TfgRegionPicker-demo.mp4
    Спасибо за внимание.
  14. Yaroslav Brovin

    Общая
    Hello Delphi developers,
    Black Friday is very soon and we want to please you with a good discount on new FGX Native licenses, which will be valid between 11-15 November 2023.
    Discount for English version - 150$. When placing an order, use the promo code BLACKFRIDAY2023 here. Discount for Russian version - 2000₽. Thank you and have a good shopping,
    FGX Native team
  15. Yaroslav Brovin
    Мы постоянно работаем над расширением возможностей библиотеки FGX Native. Чтобы разнообразие деталей в конструкторе росло, а создание приложений становилось все проще и быстрее. 
    В новом релизе 1.9.2.0 мы добавили два новых компонента: TfgVirtualListPicker и TfgNumberListPicker. Эти компоненты позволяют выбрать одно значение из списка при помощи крутящегося барабана. В основном такие компоненты использовались при выборе даты и времени:
     
    TfgVirtualListPicker
    Этот компонент не хранит у себя элементы списка и работает подобно TfgCollectionView. А именно запрашивает все необходимые данные у разработчика через специальные события: OnGetItemsCount и OnGetItemText.
    Допустим у нас есть список городов. Для упрощения названия городов мы поместим в константный массив. У вас он может находится в базе данных или в любой другой модели данных:
    const Titles: array [0..6] of string = ('Saint-Petersburg', 'Moscow', 'Chelyabinsk', 'Sochi', 'Krasnodar', 'Voronej', 'Rostov-on-don'); Для полноценной работы компонента ему необходимо знать только:
    Количество элементов в списке. Оно указывается в событии OnGetItemsCount. procedure TFormMain.fgVirtualListPicker1GetItemCount(Sender: TObject; var ACount: Integer); begin ACount := Length(Titles); end; Текстовое название элемента, отображаемое в компоненте. Оно указывается в событии OnGetItemText. Если вы не указываете обработчик для этого события, то компонент по умолчанию использует надпись следующего формата "Item %d". Каждый элемент списка имеет свой индекс, по которому компонент получает текст элемента. procedure TFormMain.fgVirtualListPicker1GetItemText(Sender: TObject; const AItemIndex: Integer; var AText: string); begin AText := Titles[AItemIndex]; end; Это минимально необходимые действия для отображения списка:

    У компонента нет ограничения на максимально допустимое количество элементов в списке. Оно может быть абсолютно любым. Значит его можно использовать для списков любого размера.
    Когда пользователь выбирает элемент, срабатывают события OnChanged, OnItemSelected, через которые вы можете узнать индекс выделенного элемента.
    Внешний вид
    На текущий момент у компонента есть только одна настройка - это цвет текста элементов TextColor / TextColorName, которая по умолчанию завязана на цвет темы "Text\Text".
    Расширяемость
    Компонент TfgVirtualListPicker разработан для возможности создания своих расширений на его базе. Это значит, что вам не нужно реализовывать этот компонент для IDE, Android и iOS. Именно на его основе сделан следующий компонент TfgNumberListPicker.
    TfgNumberListPicker
    Этот компонент является кроссплатформенным расширением TfgVirtualListPicker и предлагает возможность выбрать вещественное число из указанного диапазона.
    Диапазон задается при помощи трех свойств: границы диапазона Min, Max и шага изменения сетки чисел Step.

    По умолчанию каждый элемент отображается в виде вещественного числа с двумя знаками после запятой. Однако, вы можете это поменять одним из любых способов:
    Указать стандартный формат через FormatKind = Integer. Указать свой произвольный форма через FormatKind = Custom и Format = 'Item %f' Указать свой обработчик события OnGetItemText. В остальном компонент позволяет использовать все возможности TfgVirtualListPicker.
  16. Yaroslav Brovin
    В этом году нас ждет большое количество новых компонентов и возможностей в библиотеке FGX Native: начиная о завершении добавления iOS платформы в библиотеки, продолжая всевозможными интеграциями мобильных сервисов (оплата, идентификация, встроенные покупки и тд) и заканчивая разработкой новых визуальных компонентов. 
    В последнем релизе 1.13.4.0 мы уже анонсировали добавление сервисов идентификации пользователя в сторонних сервисах. Сервисы идентификации позволяют получить информацию о пользователе используя его учетную запись в других сервисах, таких как GoogleId, AppleID, Facebook, VKontakte и тд. В релизе 1.13.4.0 мы разработали низкоуровневую кроссплатформенную архитектуру сервисов идентификации, которая позволяет нам и вам добавлять реализации любых сервисов идентификации пользователя. Первым таким сервисом стала реализация AppleID (TfgAppleIdAuthenticationClient). На подходе Facebook и другие сервисы. Детально о том, как установить и использовать разработанные компоненты мы поговорим в отдельных статьях.
    В этой статье нам хотелось бы поделиться одним очень мощным и полезным новым компонентом, которые позволит легко оживить ваше приложение и добавить в него сложные анимации. Речь пойдет о поддержке анимации Airbnb Lottie (https://airbnb.io/lottie)

     
    Немного о Lottie
    Lottie - это библиотека с открытым кодом от компании Airbnb для проигрывания анимации, созданной в приложении Adobe After Effects.
    Впервые дизайнеры могут создавать и доставлять красивые анимации без того, чтобы разработчик кропотливо воссоздавал их вручную.
    Анимация представляет собой текстовый файл в формате JSON, в котором содержится описание построения векторного изображения с анимацией. Благодаря чему, файл анимация весит очень мало при этом не теряя в качестве. 
    В библиотеку добавлен новый компонент TfgLottieImage, который позволяет отобразить Lottie анимацию.
    Как использовать?
    1. Для начала надо раздобыть файл анимации. Можно поискать готовые анимации от дизайнеров на ресурсе https://lottiefiles.com/ .

    Помимо этого ресурса, есть еще и другие. Например, ресурс по встраиванию анимированных иконок: https://lordicon.com/ 

    После выбора интересуемой анимации, скачиваем анимацию в формате Lottie JSON. Например, такая анимация ракеты весит всего 25 КБ:
    2022-02-15-00-47-28.mp4
    2. После этого добавляем этот файл анимации в дизайнер ресурсов FGX Native. Добавлен новый тип ресурса "Lottie Анимация":

    После создания нового ресурса, загружаем скаченный ранее файл. В результате чего дизайнер отобразит вам ваш файл анимации. Укажите ресурсу понятное имя, например "Animations\Red Rocket".

    Разместив на форме новый компонент TfgLottieImage, укажите ресурс анимации через свойство AnimationName. 

    Это все, что требуется, чтобы запустить ракету в космос. Запустив приложение на Android или iOS вы увидите летящую ракету.
    Lottie-Demo-Sample.mp4
    Демонстрационный пример
    В поставку включен новый демо-пример "Компоненты" -> "TfgLottieImage" -> "Проигрыватель Lottie анимации".
    Настройки 
    Компонент предлагает базовые настройки.
    ImageMode - Режим отображения изображения (растянуть, вписать, заполнить). Loop - нужно ли запускать анимацию по кругу. Speed - коэффициент скорости. Например: 2 - проигрывать анимацию в два раза быстрее, 0.5 - в два раза медленнее. Duration - длительность анимации в мсек (без учета Speed). Помимо этого, вы можете проверить проигрывается ли сейчас анимация IsAnimating или управлять воспроизведением через:
    Play - начать воспроизведение с начала Stop - остановить (Progress сбрасывается на начало). Pause - приостановить Resume - продолжить с текущего места. Progress - установить текущее место воспроизведения. Коэффициент из диапазона [0..1], где 0 - это начало, 1 - конец. Итоги
    Уверены, данная новинка найдет свое применение в ваших проектах и добавит им изюминки. 
  17. Yaroslav Brovin
    Всем доброго дня,
    При разработке кросс-платформенного приложения важной базовой составляющей является определение размеров компонентов. С одной стороны нет ничего проще, чем задать желаемый размер любому компоненту библиотеки FGX Native или задать правило выравнивания компонентов при помощи FlexBox. Однако, кросс-платформенная разработка накладывает определенные трудности в этом подходе. А именно зачастую размер компонентов зависит не только от других компонентов, но и от содержимого. И если с первым отлично справляется FlexBox, то со вторым в FGX Native нужно использовать метод для расчета размеров компонентов TfgControl.MeasureSize, и будем честны, это было временное решение.
    Мы всегда стараемся сделать библиотеку FGX Native удобной для использования, чтобы разработчик мог концентрироваться в коде на бизнес логике своего приложения, а не программной "настройке" UI. Одним из грядущих улучшений будет поддержка автоматического расчета размеров компонентов. Мы внедрили на нижнем уровне поддержку Autosize. Теперь на уровне появится новое свойство TfgControl.Autosize, которое отвечает за то, что именно нужно автоматически посчитать: ширину, высоту или обе величины. Если в ходе выравнивания компонента система понимает, что компонент имеет фиксированный размер, то в этом случае компонент может автоматически его рассчитать. Так например задание Autosize = [Height] для TfgTraсkBar автоматически посчитает нужную высоту компонента, а вот указание Autosize = [Width] для этого компонента ни на что не повлияет. 

    Не смотря на то, что сам механизм внедрен на нижнем уровне, реально это свойство в ближайшем релизе будет доступно только для нескольких компонентов: TfgSwitch, TfgTrackBar, TfgNavigationBar и возможно TfgLabel. Если с TfgSwitch и TfgTrackBar в целом понятно,  то вот на счет TfgNavigationBar  стоит отдельно отметить, что теперь этот компонент умеет в автоматическом режиме расчета высоты учитывать отступы у формы SafeArea и наличие заголовка и подзаголовка. И если раньше необходимо было программно отслеживать изменения отступов области SafeArea в TfgForm.OnSafeAreaChanged и на основании значений отступов вычислять руками высоту панели навигации, то сейчас все это будет происходить автоматически.



    Данное нововведение особенно актуально в свете предстоящего появления iOS. Так как в iOS используются другие шрифты, размеры текста и размеры компонентов. И чтобы сделать действительно универсальное приложение без этой функциональности будет очень сложно.
    Всем хорошей рабочей недели!
  18. Yaroslav Brovin
    Добрый день,
    Мы выложили запись прошедшего 18 февраля вебинара по теме "Пробная сессия вопросов и ответов по FGX Native". Видео доступно на нашем YouTube канале.
    На вебинаре обсуждались следующие темы:
     Android SDK/NDK; Сервисы локации; Асинхронная загрузка изображений в списках; Планы на ближайшие релизы.  
     
  19. Yaroslav Brovin

    Общая
    Скоро выйдет релиз 1.9.4.0 с интересными и полезными нововведениями для наших пользователей. Обо все по порядку.
    Push-уведомления
    Если вы уже использовали push-уведомления в своих приложениях, то, возможно, вы обратили внимание, что архитектура сервисов push-уведомлений в RTL с одной стороны довольно гибкая, с другой стороны требует написание определенного количество лишнего кода, которого можно было бы избежать. По этой причине мы разработали новый компонент для работы с push-уведомлениями TfgPushNotificationService. Это высокоуровневая обертка на абстракциями push-сервисов в RTL, которая облегчает работу с уведомлениями в приложении FGX Native.
       
    Для того, чтобы начать принимать push-уведомления в вашем приложении достаточно:
    Выполнить первичную настройку сервисов (Firebase для Android или Apple Push Notification Service для iOS). Бросить компонент на форму и написать обработчик для события OnPushNotificationReceived, которое вызывается при приеме уведомления. Это минимальный набор шагов, который для этого требуется. Если вам необходимо добавить диагностическую информацию, получить токен устройства и тд, то вы можете воспользоваться остальными событиями этого компонента.
    ServiceName - это название сервиса push-уведомлений, которые реализован в библиотеке.  "fcm" - сервис по приему уведомлений через Firebase (Android) "aps" - сервис по приему уведомлений через Apple Push Notification Service (iOS). "Default" - автоматический выбор сервиса в зависимости от текущей платформы. Для Android - "fcm", для iOS - "aps".  Мы обновили демо проект по использованию push-уведомлений: "Push-уведомления" -> "Получение Push-уведомления".
    Помимо этого мы полностью отладили реализацию Push-уведомлений для iOS и теперь вы можете использовать ее в своих приложениях.
     
    TfgPageControl (важно!)
    В этом обновлении мы пересмотрели работу событий OnChanged и OnPageSelected. Теперь эти события вызываются только при смене вкладок пользователем интерактивным путем. События не срабатывают при любом программном изменении вкладок (добавление, удаление, очистка, переключение и тд). Поэтому, если у вас есть логика, которая основывается на постоянном вызове этих событий при любых изменения (интерактивных и программных), то во всех местах, где вы выполняете программное изменение вкладок, вам необходимо вызвать вручную новый метод TfgPageControl.Changed, это приведет к немедленному срабатыванию данных событий. 
    В таком подходе есть много достоинств:
    Защита от рекурсивных вызовов. Если при переключении вкладок, вы выполняете добавление/удаление или изменение вкладок, то такие изменения могли приводить к рекурсивному срабатыванию этих событий, что в конечном итоге обычно решается введением дополнительного флаг на для отслеживания таких изменений. Сокращение числа вызовов этих событий на любые изменения вкладок. Раньше любое добавлении/удалении вкладки, могло приводить к срабатыванию этих событий, даже когда это реально не нужно. TfgCollectionView и TfgListMenu
    Очень часто вы задавали вопросы о том, как детальнее настроить работу компонента TfgListMenu. В релизе 1.9.0.0 мы добавили ряд настроек для управления цветовой схемой данного компонента. А для тех, кому и этого не хватает, мы специально мигрировать все шаблоны стилей пунктов меню из TfgListMenu в TfgCollectionView. Новые шаблоны доступны в контекстном меню компонента TfgCollectionView -> Add Style -> Menu.

    Все стили приведены на скриншоте ниже:

    TfgVideoControl
    В этом релизе мы добавили реализацию данного компонента для iOS и добавили возможность регулировать громкость видео через новое свойство TfgVideoControl.Volume.
    TfgPhoneDialer
    Мы так же реализовали TfgPhoneDialer для iOS.
    Всем спасибо за внимание и хороших выходных .
  20. Yaroslav Brovin
    Доброго дня!
    В ближайшем релизе 1.11.2.0 всех нас ждет появление поддержки выпадающих окон TfgPopup. Если вам необходимо отобразить содержимое в виде выпадающего окна, то эта возможность непременно вам пригодится.
    Создание
    В качестве содержимого выпадающего окна можно использовать как любой визуальный компонент так и форму. Удобнее всего использовать форму, поскольку она позволяет создать содержимое выпадающего окна прямо в дизайнере. При создании формы важно указать:
    Отключить полноэкранный режим формы TfgForm.FullScreen = False. Опционально сделать фон прозрачным через TfgForm.Transparent = True . В качестве начинки окна, я сделал форму с TfgCollectionView, используемым для отображения содержимого 

    Чтобы создать выпадающее окно нужно обязательно иметь содержимое, которое вы планируете показать в этом выпадающем окне. Создание выполняется через фабрику FGX.Popup.TfgPopupFactory, как и тосты, пикеры, диалоги и тд.
    var FPopup: TfgPopup; FPopupContent: TFrameList; FPopupContent := TFrameList.Create(nil); FPopup := TfgPopupFactory.CreatePopup(FPopupContent); После создания TfgPopup становится владельцем вашего содержимого. Это значит, что содержимое будет уничтожено, как только TfgPopup будет удален. Если вы хотите сами контролировать время жизни содержимого, вы можете передать вторым параметром значение False. В этом случае, после уничтожения TfgPopup, ваше содержимое останется жить.
    FPopup := TfgPopupFactory.CreatePopup(FPopupContent, False); После создания необходимо указать размер выпадающего окна при помощи свойства TfgPopup.Size. Например, если выпадающее окно прикреплено к кнопке. то имеет смысл в качестве ширины брать ширину кнопки:
    FPopup.Size := TSizeF.Create(fgButton1.Width, 200); Отображение
    Для отображения выпадающего окна существует два одноименных метода TfgPopup.DropDown, оба которых привязывают отображение окна к указанному компоненту.
    /// <summary> /// Открывает выпадающее окно, прикрепленное к указанному компоненту <c>AAnchor</c> со смещением <c>AOffset</c>. /// </summary> procedure DropDown(const AAnchor: TfgControl; const AOffset: TPointF); overload; virtual; abstract; /// <summary>Открывает выпадающее окно, прикрепленное к указанному компоненту <c>AAnchor</c>.</summary> procedure DropDown(const AAnchor: TfgControl); overload; FPopup.DropDown(fgButton1); Если вам необходимо выполнить отображение выпадающего окна со смещением, то вы можете передать это смещение вторым параметром:
    FPopup.DropDown(fgButton1, TPointF.Create(0, 8)); Эта версия метода удобна в случаях, когда вы самостоятельно делаете внешний вид выпадающего окна и хотите добавить отступы между выпадающим окном и компонентом, вызывающим это окно.
    Для закрытия окна используйте метод Close.
    /// <summary>Закрывает выпадающее окно.</summary> procedure Close; virtual; abstract; Настройка внешнего вида
    По умолчанию TfgPopup выглядит, как TfgCardPanel с тенью и рамкой. Однако, если вы хотите сделать свой внешний вид окна, то вы можете отключить  отображение по умолчанию через свойство TfgPopup.IsTransparent. В этом случае содержимое будет отображено прямо под привязанным компонентом.
    /// <summary>Прозрачное выпадающее окно или нет?</summary> property IsTransparent: Boolean read FIsTransparent write SetIsTransparent; Если вы используете в своих приложениях темы, то вы можете использовать темы и для содержимого выпадающего окна. Для этого необходимо указать название темы в свойстве TfgPopup.ThemeName. Таким образом можно добавить поддержку темной темы и детальнее настроить внешний вид попапов.
    /// <summary>Название темы, используемой для содержимого выпадающего окна.</summary> property ThemeName: TfgThemeName read FThemeName write SetThemeName; ios.mp4 android.mp4  
    События
    В TfgPopup поддерживается два типа событий, OnOpened и OnClosed, вызываемые при открытии и закрытии выпадающего окна соответственно. Так же мы предлагаем две версии каждого события для использования совместно с анонимными функциями и событиями.
    /// <summary>Вызывается, когда выпадающее окно отображается на экране.</summary> property OnOpened: TNotifyEvent read FOnOpened write FOnOpened; /// <summary>Вызывается, когда выпадающее окно отображается на экране.</summary> property OnOpenedCallback: TfgCallback read FOnOpenedCallback write FOnOpenedCallback; /// <summary>Вызывается, когда выпадающее окно скрывается с экрана.</summary> property OnClosed: TNotifyEvent read FOnClosed write FOnClosed; /// <summary>Вызывается, когда выпадающее окно скрывается с экрана.</summary> property OnClosedCallback: TfgCallback read FOnClosedCallback write FOnClosedCallback;  
  21. Yaroslav Brovin
    Добрый день!
    Рады с вами поделиться будущим выступлением по FGX Native на крупнейшей Итальянской конференции по Delphi в 2022 году. Конференция пройдет 21-23 июня 2022 года. Позже будет детальная информация о выступлении.
    https://www.delphiday.it/
     
  22. Yaroslav Brovin

    Общая
    Hello Delphi developers,
    In honor of the New Year and Christmas, we are pleased to offer our future customers discounts on the purchase of FGX Native licenses, which will be valid until 15 January 2023.
    Discount for English version - 100$. When placing an order, use the promo code HAPPY2023 here. Discount for Russian version - 1500₽. Also we are anounncing 6 and 12 months subscriptions for Russian version. You can read the rates here. Thank you and happy holidays
  23. Yaroslav Brovin
    Добрый вечер,
    Начиная с FGX Native 1.15.3.0, появится новый компонент для сборка аналитики использования вашего приложения TfgFirebaseAnalytics.

    Данный компонент позволяет собрать информацию об использовании вашего приложения пользователями. Например, вы можете определить:
    Какую форму пользователи открывают чаще всего. Какие версии вашего приложения успешно запускаются. Сколько пользователей первый раз запустили приложение. Какие покупки чаще всего делают в вашем приложении. С какими компонентами чаще всего взаимодействуют.  И многое другое. Подробности о Firebase Analytics: https://firebase.google.com/docs/analytics?authuser=0
    Настройка использования
    Чтобы использовать новый компонент, необходимо выполнить общую регистрацию FGX Native приложения в Firebase консоли (Шаги такие же, как регистрация приложения для использования Push-уведомлений): 
    Настройка Firebase для iOS. Настройка Firebase для Android. Дополнительно для Android нужно вручную добавить зависимость на библиотеку "com.google.firebase:firebase-analytics:21.2.0"
    https://forum.fgx-native.com/blogs/entry/56-интеграция-android-библиотек-просто-и-быстро-с-11520/

    Если вы не выполните корректную настройку приложения, то на iOS запуск приложения закончится ошибкой старта, а в Android ни одна метрика не будет отправлена в облако Firebase. 
    Отправка событий
    Чтобы начать отправку событий необходимо разместить новый компонент TfgFirebaseAnalytics. Рекомендуем разместить его на общем дата модуле, чтобы вы могли пользоваться одним экземпляром компонента в любом месте. Однако, вы можете использовать и несколько экземпляров данного компонента.
    Как только вы размещаете его в приложении, так сразу компонент начинает автоматический сбор базовых событий в вашем iOS/Android приложении, такие как:
    Определение первого запуска приложения Статистику использования разных версий вашего приложения. Сбор сессий использования.  И тд. Такие события доступны в консоли Firebase в разделе Analytics -> Realtime.

    Помимо автоматического сбора событий, вы можете выполнять отправку своих событий.
    Каждое событие, происходящее в вашем приложении, характеризуется:
    Name - имя. Строковый код события. Firebase предлагает готовые коды событий. С ними можно познакомиться на этой странице. Вы можете использовать, как готовые коды, так и свои собственные.
    Например: событие select_content предназначено для информирования о том, что пользователь выбрал важный для вас контент (нажал на кнопку, открыл форму и тд). Parameters - параметры в формате - название параметра - значение. Каждое событие может сопровождаться соответствующими параметрами. Firebase описывает связанные с событиями параметрами, которые вы можете отправить.
    Например, событие select_content сопровождается двумя параметрами: content_type и item_id (документация) Для отправки события в компоненте есть два метода, позволяющие это сделать:
    /// <summary> /// Отправляет на сервер Firebase событие с именем <c>AName</c>, параметрами c именами <c>AKeys</c> и значениями /// <c>AValues</c>. /// </summary> procedure LogEvent(const AName: string; const AKeys: TArray<string>; const AValues: array of const); overload; /// <summary>Отправляет на сервер Firebase событие с именем <c>AName</c>.</summary> procedure LogEvent(const AName: string); overload; Чтобы узнать, что пользователь нажал на кнопку покупки в вашем приложении, необходимо добавить в обработчик вызов метода LogEvent:
    procedure TFormMain.fgButtonBuyTap(Sender: TObject); begin fbAnalytics.LogEvent('select_content', ['content_type', 'item_id'], ['Button', 'Buy subscription']); end; Определение открытых экранов/форм
    Помимо отправки любых событий, компонент TfgFirebaseAnalytics позволяет отправлять информацию о том, какие формы/экраны открывает пользователь. Чтобы воспользоваться данной возможностью, необходимо в момент отображения формы передавать на сервер Firebase информацию о текущей форме.
    /// <summary> /// Задает текущее название экрана, которое определяет текущий визуальный контекст в вашем приложении. /// Это помогает определить области в вашем приложении, где пользователи проводят свое время и как они /// взаимодействуют с вашим приложением. /// </summary> procedure SetCurrentScreen(const AScreenName: string; const AScreenClassName: string); Для удобства отслеживания, когда форма появляется на переднем плане, в FGX Native 1.15.3.0 для формы TfgForm добавлены два новых события:
    /// <summary>Вызывается, когда форма выходит на передний план на экране.</summary> /// <remarks>Не вызывается для встроенных форм-фреймов.</remarks> property OnActivate: TNotifyEvent read FOnActivate write FOnActivate; /// <summary>Вызывается, когда форма уходит с переднего плана на экране.</summary> /// <remarks>Не вызывается для встроенных форм-фреймов.</remarks> property OnDeactivate: TNotifyEvent read FOnDeactivate write FOnDeactivate; Именно их мы рекомендуем использовать для указания текущего экрана в Firebase Analytics.
    Например, код по отображению главной формы может выглядеть так:
    procedure TFormMain.fgFormActivate(Sender: TObject); begin Shared.fbAnalytics.SetCurrentScreen('Main form', ClassName); end; Первым параметром указывание название экрана, вторым - класс. 
    Общие настройки
    Помимо главной задачи отправления статистики в Firebase Analyitcs, вы можете выполнять базовые настройки компонента:
    /// <summary> /// Удаляет все аналитические данные для этого приложения с устройства и сбрасывает идентификатор экземпляра приложения. /// </summary> procedure ResetAnalyticsData; /// <summary> /// Включен ли сбор аналитики для этого приложения на данном устройстве. Этот параметр сохраняется во всех сеансах /// приложения. По умолчанию он включен. /// </summary> property Enabled: Boolean read FEnabled write SetEnabled default DefaultEnabled; /// <summary>Уникальный идентификатор текущего пользователя.</summary> property UserId: string read FUserId write SetUserId; /// <summary>Задает продолжительность бездействия, которая завершает текущий сеанс.</summary> property SessionTimeoutDuration: Integer read FSessionTimeoutDuration write SetSessionTimeoutDuration default DefaultSessionTimeoutDuration; С остальными подробности использования вы можете ознакомиться в официальной документации Firebase:  https://firebase.google.com/docs/analytics?authuser=0
    Спасибо
  24. Yaroslav Brovin
    Добрый день, уважаемые разработчики. 
    Продолжаем обзор новых возможностей 1.16.0.0 и в этой статье поговорим об улучшениях связанных с изображениями.
    В этом релизе мы добавили вспомогательные методы для решения часто встречаемых задач: таких как кадрирование, превью, изменение размера, повороты и перекрашивание. 
    Для того, чтобы воспользоваться методами необходимо подключить модуль "FGX.Bitmap.Helpers" в секцию uses. 
    Кадрирование - Crop
    Данные методы позволяют вырезать из исходного битмапа указанную область.
    Предлагается два метода:
    CreateCropped - создает новый экземпляр битмапа, не изменяя исходный Crop - модифицирует исходный экземпляр битмапа. /// <summary> /// Создает новое изображение сформированное из указанной области. <c>ARegion</c> - физические координаты области. /// </summary> function CreateCropped(const ARegion: TRect): TfgBitmap; /// <summary>Выполняет вырезание указанной области изображения. <c>ARegion</c> - физические координаты области.</summary> /// <remarks> /// <para> /// Если координаты области <c>ARegion</c> выходят за пределы физического размера изоюбражения, то они отсекаются /// по размеру изображения. /// </para> /// <para> /// Если результирующая область имеет нулевой размер, то текущий битмап принимает нулевой размер. /// </para> /// </remarks> procedure Crop(const ARegion: TRect); Повороты - Rotate
    Методы поворота позволяют повернуть изображение на углы (90 - 180 - 270) градусов.
    Предлагается два метода:
    CreateRotated - создает новый экземпляр битмапа, не изменяя исходный Rotate - модифицирует исходный экземпляр битмапа и выполняет поворот. /// <summary>Созадет новое изоюражение и поворачивает его на указанный угол.</summary> function CreateRotated(const AAngle: TfgRotation): TfgBitmap; /// <summary>Поворачивает изображение на указанный угол.</summary> procedure Rotate(const AAngle: TfgRotation); Отражение - Flip
    Два метода по симметричному отражению исходного экземпляра битмапа относительно вертикальной и горизонтальной осей.
    /// <summary>Симметричное отражение изображения по горизонтальной оси.</summary> procedure FlipHorizontal; /// <summary>Симметричное отражение изображения по вертикальной оси.</summary> procedure FlipVertical; Создание превью - Thumbnail
    Очень часто требуется получить из исходного битмапа большого размера превью ограниченного размера. Для этого мы предлагаем метод:
    /// <summary> /// Создает миниатюрное изображение, которое может использовать в виде превью. Изображение вписывается в /// указанный размер с сохранение пропорций и центрируется. /// </summary> /// <remarks>Если передается нулевой размер, то метод поднимает исключение.</remarks> function CreateThumbnail(const ASize: TSize): TfgBitmap; Перекрашивание - Tint
    Методы по перекрашиванию исходного экземпляра изображения в указанный цвет.
    Предлагается два метода:
    CreateTinted - создает новый экземпляр битмапа, не изменяя исходный ApplyTintColor - модифицирует исходный экземпляр битмапа и выполняет поворот. /// <summary>Создает новое изображение и применяет указанный оттенок.</summary> function CreateTinted(const ATintColor: TAlphaColor): TfgBitmap; /// <summary>Задает оттенок изображения. Операция не отменяемая.</summary> procedure ApplyTintColor(const ATintColor: TAlphaColor); Мета информация EXIF
    Помимо вспомогательных методов по трансформации битмапов зачастую требуется выполнить нормализацию угла поворота изображения. Когда пользователь делает снимок с камеры, то камера сохраняет файл в ориентации фото сенсора, который не обязательно совпадает с текущей ориентаций экрана. Дополнительно, камера используя мета информацию EXIF помещает в нее ориентацию съемки фотографии. Поэтому при попытке загрузить такое изображение в FGX Native битмап будет отображаться в ориентации съемки с сенсора. Для того, чтобы выполнить коррекцию угла поворота для отображения мы предлагаем новое API для извлечения EXIF информации из файла. Типовой пример использования может выглядеть так:
    uses FGX.Exif; var Properties: TfgExifProperties; if TfgExif.TryLoad(AFileName, Properties) then begin // Properties.Rotation; // Properties.Rotation.ToRotation - возвращает TfgRotation, который можно передать в метод TfgBitmap.Rotate. end; На текущий момент мы извлекаем только ориентацию. Но если вам требуются получать дополнительную EXIF информацию, дайте знать, мы добавим.
    Уверены данные нововведения позволят вам сократить ваш код.
    Спасибо за внимание
  25. Yaroslav Brovin

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

    В FGX Native данное уведомление представлено классом FGX.Toasts.TfgToast, позволяющим его отобразить на экране. В релизе 1.9.2.0 мы реализовали тосты для iOS, а так же выполнили ряд улучшений в архитектуре управления TfgToast, сделав ее такой же единообразной, как и в диалогах, пикерах и других сервисах.
    Новая архитектура
    Теперь вся работа по созданию TfgToast отведена фабрике TfgToastFactory. Через нее можно, как создать экземпляр уведомления для ручной настройки, так и просто отобразить уведомление.
    Быстро отобразить уведомление можно теперь так:
    TfgToastFactory.Show('Hello world!'); Было:
    TfgToast.Show('Hello world!'); Если вы хотите по какой-то причине вручную управлять уведомлением, вы можете его создать через метод:
    Toast := TfgToastFactory.CreateToast; Было:
    Toast := TfgToast.Create('Hello world!'); Однако, в этом случае, уничтожение уведомление лежит на ваших плечах.
    Обратная совместимость
    Мы временно оставили старые методы по отображению тостов. Однако, рекомендуем вам, по возможности перейти на новый вариант использования тостов через TfgToastFactory. Поскольку старый способы могли вводить разработчиков в заблуждение, так как метод TfgToast.Create совпадает по смыслу с конструктором, однако, им не является.
×
×
  • Create New...