How can I use the Google Maps APIs in a JavaFX Desktop Application?

后端 未结 2 1376
小蘑菇
小蘑菇 2021-01-02 02:45

I would like to start working on a JavaFX desktop app that will work heavily (if all goes well) with the Google Maps APIs. I\'ve been having a more difficult time getting st

2条回答
  •  攒了一身酷
    2021-01-02 02:52

    You could use some of the API's without a webview; just use the Google client libraries...

    Add these dependencies:

    And for example, using the Places API:

    And you can get this:

    Just make sure you add your Google API key and enable billing... otherwise it won't let you do more than 1 poll a day.

    private static final String API_KEY = add API KEY HERE;

    Main.java

    import com.google.maps.model.AddressComponentType;
    import com.google.maps.model.PlaceDetails;
    import javafx.application.Application;
    import javafx.event.ActionEvent;
    import javafx.event.Event;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.control.Label;
    import javafx.scene.control.MenuItem;
    import javafx.scene.control.TextField;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    import org.apache.commons.lang3.StringUtils;
    import test.AutoCompleteAddressField.AddressPrediction;
    
    public class Main extends Application
    {
    
        @Override
        public void start(Stage primaryStage)
        {
    
            AutoCompleteAddressField text = new AutoCompleteAddressField();
    
            TextField streetField = new TextField();
            streetField.setPromptText("Street");
    
            TextField postalField = new TextField();
            postalField.setPromptText("PostalCode");
    
            TextField cityField = new TextField();
            cityField.setPromptText("City");
    
            TextField provinceField = new TextField();
            provinceField.setPromptText("Province");
    
            TextField countryField = new TextField();
            countryField.setPromptText("Country");
    
            Button clearButton = new Button("Clear");
    
            clearButton.setOnAction(e ->
            {
                text.clear();
                text.getEntries().clear();
                streetField.clear();
                postalField.clear();
                cityField.clear();
                provinceField.clear();
                countryField.clear();
    
            });
    
            text.getEntryMenu().setOnAction((ActionEvent e) ->
            {
                ((MenuItem) e.getTarget()).addEventHandler(Event.ANY, (Event event) ->
                {
                    if (text.getLastSelectedObject() != null)
                    {
                        text.setText(text.getLastSelectedObject().toString());
                        PlaceDetails place = AutoCompleteAddressField.getPlace((AddressPrediction) text.getLastSelectedObject());
                        if (place != null)
                        {
                            streetField.setText(
                                    StringUtils.join(
                                            AutoCompleteAddressField.getComponentLongName(place.addressComponents, AddressComponentType.STREET_NUMBER),
                                            " ",
                                            AutoCompleteAddressField.getComponentLongName(place.addressComponents, AddressComponentType.ROUTE))
                            );
    
                            postalField.setText(AutoCompleteAddressField.getComponentLongName(place.addressComponents, AddressComponentType.POSTAL_CODE));
                            cityField.setText(AutoCompleteAddressField.getComponentLongName(place.addressComponents, AddressComponentType.LOCALITY));
                            provinceField.setText(AutoCompleteAddressField.getComponentLongName(place.addressComponents, AddressComponentType.ADMINISTRATIVE_AREA_LEVEL_1));
                            countryField.setText(AutoCompleteAddressField.getComponentLongName(place.addressComponents, AddressComponentType.COUNTRY));
                        } else
                        {
                            streetField.clear();
                            postalField.clear();
                            cityField.clear();
                            provinceField.clear();
                            countryField.clear();
                        }
                    }
                });
            });
    
            VBox root = new VBox();
            root.getChildren().addAll(text, new Label(), streetField, postalField, provinceField, countryField, clearButton);
    
            Scene scene = new Scene(root, 300, 250);
    
            primaryStage.setTitle("Test Google Places API");
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        /**
         * @param args the command line arguments
         */
        public static void main(String[] args)
        {
            launch(args);
        }
    
    }
    

    AutoCompleteAddressField.java

    import com.google.maps.GeoApiContext;
    import com.google.maps.PlaceDetailsRequest;
    import com.google.maps.PlacesApi;
    import com.google.maps.QueryAutocompleteRequest;
    import com.google.maps.errors.ApiException;
    import com.google.maps.model.AddressComponent;
    import com.google.maps.model.AddressComponentType;
    import com.google.maps.model.AutocompletePrediction;
    import com.google.maps.model.PlaceDetails;
    import java.io.IOException;
    import java.util.TreeSet;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javafx.application.Platform;
    import javafx.beans.value.ObservableValue;
    
    public class AutoCompleteAddressField extends AutoCompleteTextField
    {
    
        private static final String API_KEY = add API KEY HERE;
    
        public AutoCompleteAddressField()
        {
            super(new TreeSet<>((AddressPrediction o1, AddressPrediction o2) -> o1.toString().compareTo(o2.toString())));
    
            textProperty().addListener((ObservableValue o, String oldValue, String newValue) ->
            {
                if (newValue != null && !newValue.isEmpty())
                {
                    new Thread(() ->
                    {
                        AutocompletePrediction[] predictions = getPredictions(getText());
    
                        Platform.runLater(() ->
                        {
                            getEntries().clear();
                            for (AutocompletePrediction prediction : predictions)
                            {
                                getEntries().add(new AddressPrediction(prediction));
                            }
                        });
                    }).start();
    
                }
            });
        }
    
        public class AddressPrediction
        {
    
            private final AutocompletePrediction prediction;
    
            public AddressPrediction(AutocompletePrediction prediction)
            {
                this.prediction = prediction;
            }
    
            @Override
            public String toString()
            {
                return prediction.description;
            }
    
            protected AutocompletePrediction getPrediction()
            {
                return this.prediction;
            }
    
        }
    
        public static PlaceDetails getPlace(AddressPrediction prediction)
        {
            if (prediction != null && prediction.getPrediction() != null && !prediction.getPrediction().placeId.isEmpty())
            {
                PlaceDetailsRequest query = PlacesApi.placeDetails(new GeoApiContext.Builder().apiKey(API_KEY).build(), prediction.getPrediction().placeId);
                return query.awaitIgnoreError();
            }
            return null;
        }
    
        public static AutocompletePrediction[] getPredictions(String userInput)
        {
    
            QueryAutocompleteRequest query = PlacesApi.queryAutocomplete(new GeoApiContext.Builder()
                    .apiKey(API_KEY)
                    .build(), userInput);
            try
            {
                return query.await();
            } catch (ApiException | InterruptedException | IOException ex)
            {
                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
            }
            return new AutocompletePrediction[0];
    
        }
    
        public static String getComponentLongName(AddressComponent[] components, AddressComponentType type)
        {
    
            for (AddressComponent component : components)
            {
                for (AddressComponentType types : component.types)
                {
                    if (types.equals(type))
                    {
                        return component.longName;
                    }
                }
    
            }
            return "";
        }
    }
    

    AutoCompleteTextField.java

    import javafx.beans.value.ObservableValue;
    import javafx.event.ActionEvent;
    import javafx.geometry.Side;
    import javafx.scene.control.ContextMenu;
    import javafx.scene.control.CustomMenuItem;
    import javafx.scene.control.TextField;
    
    import java.util.LinkedList;
    import java.util.List;
    import java.util.SortedSet;
    import java.util.TreeSet;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    import javafx.beans.property.ObjectProperty;
    import javafx.beans.property.SimpleObjectProperty;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.scene.text.Text;
    import javafx.scene.text.TextFlow;
    
    /**
     * This class is a TextField which implements an "autocomplete" functionality,
     * based on a supplied list of entries.

    * * If the entered text matches a part of any of the supplied entries these are * going to be displayed in a popup. Further the matching part of the entry is * going to be displayed in a special style, defined by * {@link #textOccurenceStyle textOccurenceStyle}. The maximum number of * displayed entries in the popup is defined by * {@link #maxEntries maxEntries}.
    * By default the pattern matching is not case-sensitive. This behaviour is * defined by the {@link #caseSensitive caseSensitive} * .

    * * The AutoCompleteTextField also has a List of * {@link #filteredEntries filteredEntries} that is equal to the search results * if search results are not empty, or {@link #filteredEntries filteredEntries} * is equal to {@link #entries entries} otherwise. If * {@link #popupHidden popupHidden} is set to true no popup is going to be * shown. This list can be used to bind all entries to another node (a ListView * for example) in the following way: *

     * 
     * AutoCompleteTextField auto = new AutoCompleteTextField(entries);
     * auto.setPopupHidden(true);
     * SimpleListProperty filteredEntries = new SimpleListProperty(auto.getFilteredEntries());
     * listView.itemsProperty().bind(filteredEntries);
     * 
     * 
    * * @author Caleb Brinkman * @author Fabian Ochmann * @param */ public class AutoCompleteTextField extends TextField { private final ObjectProperty lastSelectedItem = new SimpleObjectProperty<>(); /** * The existing autocomplete entries. */ private final SortedSet entries; /** * The set of filtered entries:
    * Equal to the search results if search results are not empty, equal to * {@link #entries entries} otherwise. */ private ObservableList filteredEntries = FXCollections.observableArrayList(); /** * The popup used to select an entry. */ private ContextMenu entriesPopup; /** * Indicates whether the search is case sensitive or not.
    * Default: false */ private boolean caseSensitive = false; /** * Indicates whether the Popup should be hidden or displayed. Use this if * you want to filter an existing list/set (for example values of a * {@link javafx.scene.control.ListView ListView}). Do this by binding * {@link #getFilteredEntries() getFilteredEntries()} to the list/set. */ private boolean popupHidden = false; /** * The CSS style that should be applied on the parts in the popup that match * the entered text.
    * Default: "-fx-font-weight: bold; -fx-fill: red;" *

    * Note: This style is going to be applied on an * {@link javafx.scene.text.Text Text} instance. See the JavaFX CSS * Reference Guide for available CSS Propeties. */ private String textOccurenceStyle = "-fx-font-weight: bold; " + "-fx-fill: rgb(66,139,202);"; /** * The maximum Number of entries displayed in the popup.
    * Default: 10 */ private int maxEntries = 10; /** * Construct a new AutoCompleteTextField. * * @param entrySet */ public AutoCompleteTextField(SortedSet entrySet) { super(); this.entries = (entrySet == null ? new TreeSet<>() : entrySet); this.filteredEntries.addAll(entries); entriesPopup = new ContextMenu(); textProperty().addListener((ObservableValue observableValue, String s, String s2) -> { if (getText() == null || getText().length() == 0) { filteredEntries.clear(); filteredEntries.addAll(entries); entriesPopup.hide(); } else { LinkedList searchResult = new LinkedList<>(); //Check if the entered Text is part of some entry String text1 = getText(); Pattern pattern; if (isCaseSensitive()) { pattern = Pattern.compile(".*" + text1 + ".*"); } else { pattern = Pattern.compile(".*" + text1 + ".*", Pattern.CASE_INSENSITIVE); } for (S entry : entries) { Matcher matcher = pattern.matcher(entry.toString()); if (matcher.matches()) { searchResult.add(entry); } } if (!entries.isEmpty()) { filteredEntries.clear(); filteredEntries.addAll(searchResult); //Only show popup if not in filter mode if (!isPopupHidden()) { populatePopup(searchResult, text1); if (!entriesPopup.isShowing()) { entriesPopup.show(AutoCompleteTextField.this, Side.BOTTOM, 0, 0); } } } else { entriesPopup.hide(); } } }); focusedProperty().addListener((ObservableValue observableValue, Boolean aBoolean, Boolean aBoolean2) -> { entriesPopup.hide(); }); } /** * Get the existing set of autocomplete entries. * * @return The existing autocomplete entries. */ public SortedSet getEntries() { return entries; } /** * Populate the entry set with the given search results. Display is limited * to 10 entries, for performance. * * @param searchResult The set of matching strings. */ private void populatePopup(List searchResult, String text) { List menuItems = new LinkedList<>(); int count = Math.min(searchResult.size(), getMaxEntries()); for (int i = 0; i < count; i++) { final String result = searchResult.get(i).toString(); final S itemObject = searchResult.get(i); int occurence; if (isCaseSensitive()) { occurence = result.indexOf(text); } else { occurence = result.toLowerCase().indexOf(text.toLowerCase()); } if (occurence < 0) { continue; } //Part before occurence (might be empty) Text pre = new Text(result.substring(0, occurence)); //Part of (first) occurence Text in = new Text(result.substring(occurence, occurence + text.length())); in.setStyle(getTextOccurenceStyle()); //Part after occurence Text post = new Text(result.substring(occurence + text.length(), result.length())); TextFlow entryFlow = new TextFlow(pre, in, post); CustomMenuItem item = new CustomMenuItem(entryFlow, true); item.setOnAction((ActionEvent actionEvent) -> { lastSelectedItem.set(itemObject); entriesPopup.hide(); }); menuItems.add(item); } entriesPopup.getItems().clear(); entriesPopup.getItems().addAll(menuItems); } public S getLastSelectedObject() { return lastSelectedItem.get(); } public ContextMenu getEntryMenu() { return entriesPopup; } public boolean isCaseSensitive() { return caseSensitive; } public String getTextOccurenceStyle() { return textOccurenceStyle; } public void setCaseSensitive(boolean caseSensitive) { this.caseSensitive = caseSensitive; } public void setTextOccurenceStyle(String textOccurenceStyle) { this.textOccurenceStyle = textOccurenceStyle; } public boolean isPopupHidden() { return popupHidden; } public void setPopupHidden(boolean popupHidden) { this.popupHidden = popupHidden; } public ObservableList getFilteredEntries() { return filteredEntries; } public int getMaxEntries() { return maxEntries; } public void setMaxEntries(int maxEntries) { this.maxEntries = maxEntries; } }

提交回复
热议问题