我想开始研究一个JavaFX桌面应用程序,该应用程序将使用Google Maps API进行大量工作(如果一切顺利)。我开始时遇到的困难比我想象的要困难,而且此时我只是想请某人提供一些代码示例,这些代码只需加载地图并在其上固定几个位置即可。任何帮助都会非常感激。到目前为止我能做的最好的事情就是在WebView中加载maps.google.com,这显然根本不涉及API,现在真的没有任何用处。
答案 0 :(得分:6)
我只是想请某人提供一些代码示例,只需加载地图并在其上固定一些位置。
其中一种方法是使用HTML,Javascript和JavaFX的组合。在html和javascript中创建一个谷歌地图实例,然后使用WebEngine类调用JavaFX程序中的URL。 WebEngine加载网页,创建文档模型,根据需要应用样式,并在页面上运行JavaScript。之后,您将使用WebView显示谷歌地图内容,如:
final WebEngine webEngine = new WebEngine(getClass().getResource("googlemap.html").toString());
final WebView webView = new WebView(webEngine);
这是一个让你入门的link with sample codes。
答案 1 :(得分:0)
您可以在没有网络视图的情况下使用某些API;只需使用Google客户端库...
添加以下依赖项:
例如,使用Places API:
您可以得到这个:
请确保您添加了Google API密钥并启用了结算功能...否则,一天最多只能进行1次轮询。
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<? extends String> 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.<p>
*
* 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}.<br>
* By default the pattern matching is not case-sensitive. This behaviour is
* defined by the {@link #caseSensitive caseSensitive}
* .<p>
*
* 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:
* <pre>
* <code>
* AutoCompleteTextField auto = new AutoCompleteTextField(entries);
* auto.setPopupHidden(true);
* SimpleListProperty filteredEntries = new SimpleListProperty(auto.getFilteredEntries());
* listView.itemsProperty().bind(filteredEntries);
* </code>
* </pre>
*
* @author Caleb Brinkman
* @author Fabian Ochmann
* @param <S>
*/
public class AutoCompleteTextField<S> extends TextField
{
private final ObjectProperty<S> lastSelectedItem = new SimpleObjectProperty<>();
/**
* The existing autocomplete entries.
*/
private final SortedSet<S> entries;
/**
* The set of filtered entries:<br>
* Equal to the search results if search results are not empty, equal to
* {@link #entries entries} otherwise.
*/
private ObservableList<S> filteredEntries
= FXCollections.observableArrayList();
/**
* The popup used to select an entry.
*/
private ContextMenu entriesPopup;
/**
* Indicates whether the search is case sensitive or not. <br>
* 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. <br>
* Default: "-fx-font-weight: bold; -fx-fill: red;"
* <p>
* Note: This style is going to be applied on an
* {@link javafx.scene.text.Text Text} instance. See the <i>JavaFX CSS
* Reference Guide</i> 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.<br>
* Default: 10
*/
private int maxEntries = 10;
/**
* Construct a new AutoCompleteTextField.
*
* @param entrySet
*/
public AutoCompleteTextField(SortedSet<S> entrySet)
{
super();
this.entries = (entrySet == null ? new TreeSet<>() : entrySet);
this.filteredEntries.addAll(entries);
entriesPopup = new ContextMenu();
textProperty().addListener((ObservableValue<? extends String> observableValue, String s, String s2) ->
{
if (getText() == null || getText().length() == 0)
{
filteredEntries.clear();
filteredEntries.addAll(entries);
entriesPopup.hide();
} else
{
LinkedList<S> 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<? extends Boolean> observableValue, Boolean aBoolean, Boolean aBoolean2) ->
{
entriesPopup.hide();
});
}
/**
* Get the existing set of autocomplete entries.
*
* @return The existing autocomplete entries.
*/
public SortedSet<S> 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<S> searchResult, String text)
{
List<CustomMenuItem> 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<S> getFilteredEntries()
{
return filteredEntries;
}
public int getMaxEntries()
{
return maxEntries;
}
public void setMaxEntries(int maxEntries)
{
this.maxEntries = maxEntries;
}
}
答案 2 :(得分:-1)
您可以在没有网络视图的情况下使用某些API;只需使用Google客户端库...
添加以下依赖项:
例如,使用Places API:
您可以得到这个:
请确保您添加了Google API密钥并启用了结算功能...否则,一天最多只能进行1次轮询。
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<? extends String> 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.<p>
*
* 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}.<br>
* By default the pattern matching is not case-sensitive. This behaviour is
* defined by the {@link #caseSensitive caseSensitive}
* .<p>
*
* 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:
* <pre>
* <code>
* AutoCompleteTextField auto = new AutoCompleteTextField(entries);
* auto.setPopupHidden(true);
* SimpleListProperty filteredEntries = new SimpleListProperty(auto.getFilteredEntries());
* listView.itemsProperty().bind(filteredEntries);
* </code>
* </pre>
*
* @author Caleb Brinkman
* @author Fabian Ochmann
* @param <S>
*/
public class AutoCompleteTextField<S> extends TextField
{
private final ObjectProperty<S> lastSelectedItem = new SimpleObjectProperty<>();
/**
* The existing autocomplete entries.
*/
private final SortedSet<S> entries;
/**
* The set of filtered entries:<br>
* Equal to the search results if search results are not empty, equal to
* {@link #entries entries} otherwise.
*/
private ObservableList<S> filteredEntries
= FXCollections.observableArrayList();
/**
* The popup used to select an entry.
*/
private ContextMenu entriesPopup;
/**
* Indicates whether the search is case sensitive or not. <br>
* 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. <br>
* Default: "-fx-font-weight: bold; -fx-fill: red;"
* <p>
* Note: This style is going to be applied on an
* {@link javafx.scene.text.Text Text} instance. See the <i>JavaFX CSS
* Reference Guide</i> 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.<br>
* Default: 10
*/
private int maxEntries = 10;
/**
* Construct a new AutoCompleteTextField.
*
* @param entrySet
*/
public AutoCompleteTextField(SortedSet<S> entrySet)
{
super();
this.entries = (entrySet == null ? new TreeSet<>() : entrySet);
this.filteredEntries.addAll(entries);
entriesPopup = new ContextMenu();
textProperty().addListener((ObservableValue<? extends String> observableValue, String s, String s2) ->
{
if (getText() == null || getText().length() == 0)
{
filteredEntries.clear();
filteredEntries.addAll(entries);
entriesPopup.hide();
} else
{
LinkedList<S> 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<? extends Boolean> observableValue, Boolean aBoolean, Boolean aBoolean2) ->
{
entriesPopup.hide();
});
}
/**
* Get the existing set of autocomplete entries.
*
* @return The existing autocomplete entries.
*/
public SortedSet<S> 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<S> searchResult, String text)
{
List<CustomMenuItem> 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<S> getFilteredEntries()
{
return filteredEntries;
}
public int getMaxEntries()
{
return maxEntries;
}
public void setMaxEntries(int maxEntries)
{
this.maxEntries = maxEntries;
}
}