Базовым UI фреймворком для нашего приложения был выбрана JavaFX. JavaFX прекрасно показала себя, в этой же статье мы хотели сконцентрироваться на одном компоненте JavaFX — WebView.
Мы при разработке нашего приложения — интерфейса COLT использовали набирающий среди девелоперов подход, когда часть компонентов реализуется на JavaScript/HTML.
Компонент на базе HTML/JS — это обычный Java класс, обычный JavaFx компонент с лайаутом — HBox или просто Box, который содержит в себе экземляр компонента Webview.
Как создать экземпляр webkit в JavaFx и подгрузить HTML
WebView webView = new WebView(); WebEngine engine = webView.engine engine.load(this.getClass().getResource("html/webview.html").toExternalForm())
Что мы получили использовав web технологии в нашем приложении.
Тонны готовых решений
jQuery, D3 покрывает почти все наши задачи. Нужно готовое решение — находим через гугл за 5 минут. Здорово что аналогами элементов интерфейса заполнен под завязку весь интернет. Причем все в открытом доступе. Можно подглядеть идею и взять ее реализацию.
Дешево
На Java такое же, что мы реализовали на JS/HTML написать было бы непросто.
Например, этот компонент для добавления путей. За основу мы взяли движок для tag-ов и хорошенечко его «допилили». Получилась очень умная и приятная штука. Умеет редактировать в режиме «тэгов», а по двойному клику еще позволяет просто работать с текстом. Работа с клипбоардом.
До этого, в предыдущей версии интерфейса, у нас использовался уродливый list-view. Наш новый fileset прост, компактен, современен. Есть идеи как еще можно расширять его фунциональность.
Второй пример — log-view.
Сначала мы хотели сделать компонент на основе ListView. Особых проблем с со скинованием мы не получили, но все же компонент получился малопригодным для реального использования пользователями. Например, нельзя было выделить логи как текст, сразу несколько блоков, реализация компонента грозила вылиться в немалый объем кода. Компонент на HTML получился легким и легко расширяемым. Нужно отметить что JavaFX использует GPU для рендеринга DOM, поэтому компонент получился достаточно производительным. Компонент мы планируем расширить поиском по логам, фильтрами и т.д. Все это мы планируем реализовать средствами JavaScript/CSS.
Возможно мы просто не настолько хороши в Java-UI, но нам действительно было сложно писать на Java компоненты со сложной интерактивностью. Там где нужен готовый компонент, пример который можно «нагуглить» — да все легко и просто. Связать через байндинг данные и «вьюхи» — опять очень просто и привычно (наш предыдущий опыт — Adobe Flex). Но когда дело касается чего-то выходящего за рамки стандартных, описанных в документации кейсов — мы теряли слишком много времени на ресерч.
Мы выработали для себя следующую схему — пытаемся сделать через JavaFx-компоненты, и если «упираемся в стену», или излишне усложняется реализация, то пишем на HTML. Такой подход значительно ускорил нашу разработку.
Кроссплатфоменность
Движок Webview в JavaFX — это webkit. На любой платформе, которую поддерживает Java. Мы практически не тратили время на кросс платфоменность.
Java-Javascript Bridge
Связка java-javascript, мост между ними позволяет делать вызовы методов Java-классов из HTML, а так же вызывать код JavaScript из Java. Простой пример код на Java:
private void clear() { JSObject windowObject = (JSObject)webView.getEngine().executeScript("window"); windowObject.call("test", new ArrayList(1, 2, 3)); }
А этот код JavaScript который принимает данные и вызывает методы ArrayList java —
function test(list){ console.log("list.size() - " + list.size();//3 console.log("list.get(0) - " + list;//1 }
Пример конечно же не совсем реальный, синтетика, но видно, что методы вызываются, и данные принимаются.
Вы можете при загрузке html, добавить ссылку на приложение прямо в объект window.
JSObject windowObject = (JSObject) webView.engine.executeScript("window"); windowObject.setMember("app", this);
И вызывать методы Java из кода JavaScript —
app.addData($("my-input").val());
Более правильным (более безопасным) подходом является создание в Java окружении специального объекта, который ограничивает доступ, и позволяет из JavaScript вызывать в приложении только «разрешенные» методы.
windowObject.setMember("app", new JSBridge(){ public myMethod(){ MyApp.this.myMethid(); } });
Еще один пример связки через JS «alert». Нам нужно было отловить «load» html — полную инициализацию приложения. Все другие методы оказались не так надежны. Что мы сделали.
Как обычно в JavaScript через jquery:
$(function(){ alert("command:ready"); });
В Java добавили обработчик «onAlert» для webview.enging и теперь мы точно знаем что HTML загружен и инициализирован.
engine.onAlert = new EventHandler<WebEvent<String>>() { @Override void handle(WebEvent<String> event) { if("command:ready".equals(event.getData())){ //TODO: initialize } } }
Переиспользование кода
Код который мы написали для нашего приложения для компьютеров мы планируем переиспользовать для мобильной разработки. На мобильном приложении (objective-c/java) мы так же создадим компонент на на основе webview большая часть фунциональности будет будет уже готовой.
C какими сложностями мы столкнулись при работе с HTML/JS в javafx?
Первое. Жизненный цикл загрузки документа. Вызывать методы в JS можно только после полной загрузки страницы, в документации предлагается путь — отслеживать в JAVA событие смены состояния страницы. Такой подход оказался достаточно глючным. Как упоминал ранее связка события «load» на стороне html и перехват «alert» на стороне Java, решил эту проблему.
Вторая проблема, которая попила у нас кровь — это клюки с вызовами одновременно нескольких javascript функций в разных компонентах с webview. Причем такой баг вылез достаточно недавно и очень похоже на оптимизацию, которую добавили в движок webkit в javafx. В любом случае, можно получить ошибку о бесконечной рекурсии в javascript буквально на пустом месте. Такая проблема решается достаточно легко оборачиванием вызова Platform.runLater() обращений к Javascript из Java..
Третья проблема. Дебаг и логгинг.
На первом этапе работы, когда идет прототипирование и вы работаете только с HTML/JS можно тестировать просто в браузере. На этапе интерграции с Java ваш код просто перестает работать без правильного окружения. И вы все чаще и чаще начинаете собирать Java приложение для того чтобы потестировать и подправить фукнционал на JavaScript. А билд Java приложения и его запуск это долгие секунды ожидания. Какие приемы в работе мы использовали, чтобы такую долгую загрузки избежать и не терять время?
Простой и очевидный пусть — добавить «релоад» страницы по комбинации клавиш. Подправил HTML/CSS/JS — нажал F5 — содержимое WebView перегрузилось:
$(document).keydown(function (e) { if(e.keyCode == 116 /*F5*/){ location.reload(); } });
Но кроме быстрого рефреша нам понадобится просмотр логов.
Достаточно простым решением было добавить FireBug Lite —
<script type='text/javascript' src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
Но настоящим прорывом, для нас и решением большинства кейсов стало использование нашего же инструмента COLT. Да и логично, не так ли?
Как подключить? Просто добавляем в html документ следующий код
var url = "http://<address>:<port>/webview.html"; if (location.href != url) { location.href = url }
Где url — адрес по которому COLT запускает трансформированную страницу. Узнать его просто. Создаем проект COLT, указываем ссылку на нашу страницу — «Main Document» и запускаем сессию жмем «зеленую молнию».
Страница будет открыта в браузере. Копируем пусть к странице и вставляем в наш код. Окно браузера закрываем. Теперь запускаем JavaFx приложение. Пробуем изменить содержимое страницы, javascript и прямо в JavaFx приложении будет работать livecoding.
В новой версии, в настройках, в блоке «Advanced», мы добавили специальное тектовое поле с данным снипетом. Без старта проекта вы сможете скопировать данный код прямо из настроек COLT.
Логи будут появляться в окне COLT. Чтобы в следующий раз кольт открывал не браузер, а запускал ваше приложение, можно выбрать запуск не браузера, а запуск с консоли. В нашем проекте, мы просто скопировали текст из output нашей run-configuration IDEA и добавили этот вызов в COLT console luncher.
Теперь при нажатии на «зеленую бровь» будет запущено окно Java приложения.
В целом подход с редиректом внутри HTML на live-страницу универсален. Он, например, прекрасно работает и на PhoneGap. Приложение запускается на мобильное устройство (главное чтобы устройство было в той же локальной сети) и обновления доставляются без необходимости рестарта. Нужно запомонить порядок действий — и можно применять везде, где есть WebView.
Хорошей практикой стало добавлять функцию-обработчик события изменнения кода.
//@code-update function live(){ console.log("live update"); }
Аннотацию @code-update перехватывают наши AST трансформации и добавляют листенер на событие обновления кода.
Типичный кейс работы с событием обновления кода, это вызывать функции в коде, то есть то что мы обычно пишем в консоль FireBug — поменять значение или вызвать функцию, когда приложение запущено. Добавил код, сохранил — код выполнился.
Плюс удобно трейсить, выводить данные, значение которых нужно узнать в рантайм, без остановки кода, дебага, вы можете просто вывести их на консоль.
Groovy
Так же несколько выходящим за рамки темы статьи, вывод, который мы сделали при реализации нашего проекта и которым бы хотели поделиться, что писать UI лучше на языке более гибким чем Java. Мы воспользовались для этого Groovy и очень довольны. Количество кода сократилось раз в десять, а работа с XML и файловой системой упростилась так же на порядок. AST трансформации для создания Bindable свойств, наши трансформации позволили подмешивать сервисы в контроллеры, генерировать Bindable обертки для простых данных. И так далее. Что называется must have. Про использвоание Groovy для JavaFx мы пишем отдельную статью.
Планы
Подход, который мы использовали в разработке нашего настольного приложения, мы начали применять и для мобильной разработки. Мы используем PhoneGap+COLT и кейс практически ничем не отличается от JavaFX+HTML+COLT.
Выбрали PhoneGap, а не другую платформу так как уже было накоплено много готового в desktop проекте и переписывать заново на другую платфому нам показалось нецелесообразным. Если наше приложение потребует отказаться от PhoneGap, мы мы перепишем часть приложения на натив, но весь фунционал, который написан на HTML/JS мы не потеряем. Они просто будут под другому запускаться.
Сайт проекта codeoerchestra.com
ссылка на оригинал статьи http://habrahabr.ru/company/codeorchestra/blog/198776/
Добавить комментарий