Хочу рассказать о своем опыте использования Google Maps в приложении на JavaFX. Рассмотрим загрузку карты в приложение и вызов Google Maps JavaScript API v3 для загруженной карты из своего кода на Java.
Реализация
Сначала напишем код инициализации карты на javascript, который оформим ввиде html-страницы map.html. Далее по мере продвижения будем добавлять дополнительный код.
<!DOCTYPE html> <html> <head> <meta name="viewport" content="initial-scale=1.0, user-scalable=no" /> <style type="text/css"> html { height: 100% } body { height: 100%; margin: 0; padding: 0 } #map_canvas { height: 100% } </style> <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=***&sensor=false"> </script> <script type="text/javascript"> var map; var marker; function initialize() { var defLatLng = new google.maps.LatLng(59.95632093391832, 30.309906005859375); var mapOptions = { center: defLatLng, zoom: 3, mapTypeId: google.maps.MapTypeId.ROADMAP, disableDefaultUI: true, panControl: false }; map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions); marker = new google.maps.Marker({ position: defLatLng, map: map, icon: "img/Pin.png" }); } </script> </head> <body onload="initialize()"> <div id="map_canvas" style="width:100%; height:100%"></div> </body> </html>
Теперь у нас есть html-страница с картой. Ее можно открыть в любом браузере. В JavaFX есть замечательный класс для отображения веб-контента: javafx.scene.web.WebView. Воспользуемся им для отображения написанной html-странички. Напишем класс GoogleMap, который будет представлять Google Map в нашем приложении на JavaFX. Чтобы работать с картой как с любым другим JavaFX-нодом (Scene graph node), унаследуемся от класса javafx.scene.Parent.
public class GoogleMap extends Parent { public GoogleMap() { initMap(); getChildren().add(webView); } private void initMap() { webView = new WebView(); webEngine = webView.getEngine(); webEngine.load(getClass().getResource("map.html").toExternalForm()); ready = false; webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() { @Override public void changed(final ObservableValue<? extends Worker.State> observableValue, final Worker.State oldState, final Worker.State newState) { if (newState == Worker.State.SUCCEEDED) { ready = true; } } }); } private WebView webView; private WebEngine webEngine; private boolean ready; }
Простого отображения карты не достаточно. Нам хочется управлять картой и отлавливать события карты. Для обращения к функциям на javascript нам потребуется экземпляр класса netscape.javascript.JSObject, который получим в методе initCommunication(), вызываемом в конструкторе класса GoogleMap. Так же в этом методе закинем в контекст javascript кода GoogleMap.this (экземпляр класса GoogleMap), чтобы иметь возможность вызывать методы класса GoogleMap из кода на javascript.
private void initCommunication() { webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() { @Override public void changed(final ObservableValue<? extends Worker.State> observableValue, final Worker.State oldState, final Worker.State newState) { if (newState == Worker.State.SUCCEEDED) { doc = (JSObject) webEngine.executeScript("window"); doc.setMember("app", GoogleMap.this); } } }); } private JSObject doc;
Продемонстрируем вызов Java кода из javascript кода на примере отлавливания события клика по карте. Это событие, как ни странно, возникает при нажатии на карту и содержит географические координаты нажатия. Сначала напишем класс события:
public class MapEvent extends Event { public MapEvent(GoogleMap map, double lat, double lng) { super(map, Event.NULL_SOURCE_TARGET, Event.ANY); this.lat = lat; this.lng = lng; } public double getLat() { return this.lat; } public double getLng() { return this.lng; } private double lat; private double lng; }
Теперь напишем методы класса GoogleMap, ответственные за регистрацию обработчика, прием события от javasript кода и вызов обработчика:
public void setOnMapLatLngChanged(EventHandler<MapEvent> eventHandler) { onMapLatLngChanged = eventHandler; } public void handle(double lat, double lng) { if(onMapLatLngChanged != null) { MapEvent event = new MapEvent(this, lat, lng); onMapLatLngChanged.handle(event); } } private EventHandler<MapEvent> onMapLatLngChanged;
Осталось только написать обработчик события карты в javascript коде и вызвать в нем метод handle(double lat, double lng) класса GoogleMap:
function get_click_position(event){ var location = event.latLng; var lat = location.lat(); var lng = location.lng(); app.handle(lat, lng); }
Теперь напишем метод класса GoogleMap для вызова функций javascript из java кода:
private void invokeJS(final String str) { if(ready) { doc.eval(str); } else { webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() { @Override public void changed(final ObservableValue<? extends Worker.State> observableValue, final Worker.State oldState, final Worker.State newState) { if (newState == Worker.State.SUCCEEDED) { doc.eval(str); } } }); } }
Результат
Не буду отдельно пояснять методы установки типа карты, положения центра карты, положения курсора. Все эти методы есть в полных исходниках:
public class GoogleMap extends Parent { public GoogleMap() { initMap(); initCommunication(); getChildren().add(webView); setMarkerPosition(0,0); setMapCenter(0, 0); switchTerrain(); } private void initMap() { webView = new WebView(); webEngine = webView.getEngine(); webEngine.load(getClass().getResource("resources/map.html").toExternalForm()); ready = false; webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() { @Override public void changed(final ObservableValue<? extends Worker.State> observableValue, final Worker.State oldState, final Worker.State newState) { if (newState == Worker.State.SUCCEEDED) { ready = true; } } }); } private void initCommunication() { webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() { @Override public void changed(final ObservableValue<? extends Worker.State> observableValue, final Worker.State oldState, final Worker.State newState) { if (newState == Worker.State.SUCCEEDED) { doc = (JSObject) webEngine.executeScript("window"); doc.setMember("app", GoogleMap.this); } } }); } private void invokeJS(final String str) { if(ready) { doc.eval(str); } else { webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() { @Override public void changed(final ObservableValue<? extends Worker.State> observableValue, final Worker.State oldState, final Worker.State newState) { if (newState == Worker.State.SUCCEEDED) { doc.eval(str); } } }); } } public void setOnMapLatLngChanged(EventHandler<MapEvent> eventHandler) { onMapLatLngChanged = eventHandler; } public void handle(double lat, double lng) { if(onMapLatLngChanged != null) { MapEvent event = new MapEvent(this, lat, lng); onMapLatLngChanged.handle(event); } } public void setMarkerPosition(double lat, double lng) { String sLat = Double.toString(lat); String sLng = Double.toString(lng); invokeJS("setMarkerPosition(" + sLat + ", " + sLng + ")"); } public void setMapCenter(double lat, double lng) { String sLat = Double.toString(lat); String sLng = Double.toString(lng); invokeJS("setMapCenter(" + sLat + ", " + sLng + ")"); } public void switchSatellite() { invokeJS("switchSatellite()"); } public void switchRoadmap() { invokeJS("switchRoadmap()"); } public void switchHybrid() { invokeJS("switchHybrid()"); } public void switchTerrain() { invokeJS("switchTerrain()"); } public void startJumping() { invokeJS("startJumping()"); } public void stopJumping() { invokeJS("stopJumping()"); } public void setHeight(double h) { webView.setPrefHeight(h); } public void setWidth(double w) { webView.setPrefWidth(w); } public ReadOnlyDoubleProperty widthProperty() { return webView.widthProperty(); } private JSObject doc; private EventHandler<MapEvent> onMapLatLngChanged; private WebView webView; private WebEngine webEngine; private boolean ready; }
<!DOCTYPE html> <html> <head> <meta name="viewport" content="initial-scale=1.0, user-scalable=no" /> <style type="text/css"> html { height: 100% } body { height: 100%; margin: 0; padding: 0 } #map_canvas { height: 100% } </style> <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=***&sensor=false"> </script> <script type="text/javascript"> var map; var marker; function get_click_position(event){ var location = event.latLng; var lat = location.lat(); var lng = location.lng(); setMarkerPosition(lat, lng); app.handle(lat, lng); } function setMarkerPosition(lat, lng) { var clickLatLng = new google.maps.LatLng(lat, lng); marker.setPosition(clickLatLng); } function startJumping(){ marker.setAnimation(google.maps.Animation.BOUNCE); } function stopJumping(){ marker.setAnimation(google.maps.Animation.BOUNCE); } function setMapCenter(lat, lng) { var latlng = new google.maps.LatLng(lat, lng); map.setCenter(latlng); } function switchSatellite() { var mapOptions = { mapTypeId: google.maps.MapTypeId.SATELLITE }; map.setOptions(mapOptions); setLightMarkerIcon(); } function switchRoadmap() { var mapOptions = { mapTypeId: google.maps.MapTypeId.ROADMAP }; map.setOptions(mapOptions); setDarkMarkerIcon(); } function switchHybrid() { var mapOptions = { mapTypeId: google.maps.MapTypeId.HYBRID }; map.setOptions(mapOptions); setLightMarkerIcon(); } function switchTerrain() { var mapOptions = { mapTypeId: google.maps.MapTypeId.TERRAIN }; map.setOptions(mapOptions); setDarkMarkerIcon(); } function initialize() { var defLatLng = new google.maps.LatLng(59.95632093391832, 30.309906005859375); var mapOptions = { center: defLatLng, zoom: 3, mapTypeId: google.maps.MapTypeId.ROADMAP, disableDefaultUI: true, panControl: false }; map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions); google.maps.event.addListener(map, 'click', get_click_position); marker = new google.maps.Marker({ position: defLatLng, map: map, icon: "img/Pin.png" }); app.handle(0, 0); } function setDarkMarkerIcon() { marker.setIcon("img/Pin.png"); } function setLightMarkerIcon() { marker.setIcon("img/Pin_s.png"); } </script> </head> <body onload="initialize()"> <div id="map_canvas" style="width:100%; height:100%"></div> </body> </html>
Как сказал Вольтер:
«Я могу быть не согласным с Вашим мнением, но я готов отдать жизнь за Ваше право высказывать его.»
ссылка на оригинал статьи http://habrahabr.ru/post/170141/
Добавить комментарий