在 Geocoder Google API 中查找的 JComboBox
JComboBox with lookup at Geocoder Google APIs
当用户点击 VK_ENTER 进入可编辑的 JComboBox 时,我正在尝试通过查找 Google Geocoder API 来实现自定义 JComboBox。
这里是代码:
package lucasepe.desktop.arsenal.widgets;
import java.awt.Component;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.SwingConstants;
import javax.swing.SwingWorker;
import lucasepe.desktop.arsenal.models.Address;
import lucasepe.desktop.arsenal.models.Location;
import lucasepe.desktop.arsenal.utils.IOUtils;
import lucasepe.desktop.enroll.utils.UIBundle;
import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONObject;
@SuppressWarnings("serial")
public class GeocoderComboBox extends JComboBox<Location> {
static final
private Logger LOG = Logger.getLogger(GeocoderComboBox.class);
private DefaultComboBoxModel<Location> mAdapter;
public GeocoderComboBox() {
super();
mAdapter = new DefaultComboBoxModel<Location>();
setModel(mAdapter);
setEditable(true);
getEditor().getEditorComponent()
.addKeyListener(new LocationSearcher());
setRenderer(new LocationListCellRenderer());
}
private class LocationSearcher extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
String constraint = ((JTextField)getEditor().getEditorComponent()).getText();
GeocodeWorker worker = new GeocodeWorker(constraint);
worker.execute();
}
}
};
private class GeocodeWorker extends SwingWorker<List<Location>, Void> {
final
private String mConstraint;
public GeocodeWorker(String constraint) {
mConstraint = (constraint != null) ?
constraint.toString().trim() : null;
}
@Override
protected List<Location> doInBackground() throws Exception {
if (mConstraint == null || mConstraint.length() == 0)
return null;
StringBuilder url = new StringBuilder();
url.append("https://maps.googleapis.com/maps/api/geocode/json?");
url.append("language=it");
url.append("&components=")
.append(encode("country:IT|route:")).append(encode(mConstraint));
url.append("&key=").append(UIBundle.getString("google_api_key"));
ArrayList<Location> locations = new ArrayList<Location>(10);
try {
JSONObject response = IOUtils.fetchJSONObject(url.toString());
if (response != null && response.optString("status", "KO").equals("OK")) {
JSONArray ja = response.getJSONArray("results");
for (int i = 0; i < ja.length(); i++) {
JSONObject jo = ja.getJSONObject(i);
String address = jo.optString("formatted_address");
if ((address != null) && address.trim().length() > 0) {
Address poi = new Address("GOOGLE");
LOG.debug("jo: " + jo.toString());
poi.setTitle(address);
poi.setCity(getShortName("locality", jo));
JSONObject geo = jo.getJSONObject("geometry")
.getJSONObject("location");
poi.setLatitude(geo.getDouble("lat"));
poi.setLongitude(geo.getDouble("lng"));
locations.add(poi);
}
}
}
} catch (Exception err) {
LOG.error("error: " + err.getMessage(), err);
}
return locations;
}
@Override
protected void done() {
try {
List<Location> data = get();
if (data == null || data.size() == 0)
mAdapter.removeAllElements();
else {
for (Location l: data)
mAdapter.addElement(l);
}
} catch (Exception err) {
LOG.error("error: " + err.getMessage(), err);
}
}
private String getShortName(String type, JSONObject jo) {
if (type == null || type.length() == 0 || jo == null)
return null;
String result = null;
try{
JSONArray ja = jo.optJSONArray("types");
if (ja != null) {
for (int i = 0; i < ja.length(); i++) {
if (ja.getString(i).equals(type)) {
result = jo.optString("short_name");
break;
}
}
}
} catch (Exception err) {
LOG.error("err: " + err.getMessage());
}
return result;
}
private String encode(String value) {
try {
return URLEncoder.encode(value, "UTF-8");
} catch (UnsupportedEncodingException err) {
LOG.error("encoding: " + value, err);
return value;
}
}
};
private class LocationListCellRenderer extends JPanel
implements
ListCellRenderer<Location> {
private JLabel mDisplayName;
public LocationListCellRenderer() {
super();
setOpaque(true);
setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
setLayout(new VerticalFlowLayout());
mDisplayName = new JLabel();
mDisplayName.setHorizontalAlignment(SwingConstants.LEFT);
add(mDisplayName);
}
@Override
public Component getListCellRendererComponent(
JList<? extends Location> list, Location value, int index,
boolean isSelected, boolean cellHasFocus) {
mDisplayName.setText(value.toString());
return this;
}
};
}
我是这样使用它的:
package lucasepe.desktop.enroll.fragments;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerDateModel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import lucasepe.desktop.arsenal.models.Address;
import lucasepe.desktop.arsenal.models.Location;
import lucasepe.desktop.arsenal.widgets.GeocoderComboBox;
import lucasepe.desktop.enroll.database.EnrollContract.PersonaColumns;
import net.java.dev.designgridlayout.DesignGridLayout;
import net.java.dev.designgridlayout.LabelAlignment;
import org.apache.log4j.PropertyConfigurator;
@SuppressWarnings("serial")
public class EditPersonaFragment extends JPanel {
private JTextField mFirstName;
private JTextField mLastName;
private JTextField mPhone;
private JTextField mEmail;
private JSpinner mBirthDate;
private JComboBox<PersonaColumns.Gender> mGender;
//private JTextField mAddress;
private GeocoderComboBox mAddress;
private JTextField mPostalCode;
private JTextField mCity;
private JTextField mState;
private JTextField mCountry;
public EditPersonaFragment() {
super();
mFirstName = new JTextField();
mLastName = new JTextField();
mPhone = new JTextField();
mEmail = new JTextField();
mBirthDate = new JSpinner();
mBirthDate.setModel(new SpinnerDateModel());
mBirthDate.setEditor(new JSpinner.DateEditor(mBirthDate, "dd/MM/yyyy"));
mGender = new JComboBox<PersonaColumns.Gender>(PersonaColumns.Gender.values());
//mAddress = new JTextField();
mAddress = new GeocoderComboBox();
mAddress.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent event) {
if (event.getStateChange() == ItemEvent.SELECTED) {
Address item = (Address)mAddress.getSelectedItem();
mCity.setText(item.getCity());
mState.setText(item.getState());
mCountry.setText(item.getCountry());
}
}
});
mCity = new JTextField();;
mState = new JTextField();
mCountry = new JTextField();
mPostalCode = new JTextField();
DesignGridLayout layout = new DesignGridLayout(this);
layout.labelAlignment(LabelAlignment.RIGHT);
layout.row()
.grid(new JLabel("Last Name")).add(mLastName)
.grid(new JLabel("First Name")).add(mFirstName);
layout.row()
.grid(new JLabel("Phone")).add(mPhone)
.grid(new JLabel("Email")).add(mEmail);
layout.row()
.grid(new JLabel("Birthdate")).add(mBirthDate)
.grid(new JLabel("Gender")).add(mGender);
layout.emptyRow();layout.emptyRow();
layout.row().center().fill().add(new JSeparator());
layout.emptyRow();layout.emptyRow();
layout.row().grid(new JLabel("Address")).add(mAddress);
layout.row()
.grid(new JLabel("City")).add(mCity)
.grid(new JLabel("Postal Code")).add(mPostalCode);
layout.row()
.grid(new JLabel("State")).add(mState)
.grid(new JLabel("Country")).add(mCountry);
}
public static void main(String[] args) throws Exception {
PropertyConfigurator.configure("logger.properties");
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("Login Form");
frame.getContentPane().add(new EditPersonaFragment());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
});
}
}
我第一次输入地址并按回车键时,Geocoder 工作但出现此异常:
java.lang.String cannot be cast to lucasepe.desktop.arsenal.models.Address
at lucasepe.desktop.enroll.fragments.EditPersonaFragment.itemStateChanged(EditPersonaFragment.java:66)
ItemListener 似乎已被触发:"How avoid this?"
当用户在可编辑的 JComboBox 上按下回车键时,是否有更好的方法来触发地理编码器?
我在想我做错了,也许有人会很友好地指出我正确的方向。
P.S.
我是一个 Android 愿意学习桌面方式的人,所以请不要打我太重:-)
正如我看到的那样,在工作人员的工作完成后,您从组合框模型中删除了所有选项并添加了结果。
您应该跳过侦听器中的事件,方法是在模型更新之前删除侦听器并在更新之后读取侦听器,或者引入一个标志(默认情况下我们称之为 isAPI)=false。在您的侦听器中,您只能在 isAPI=false 时检查标志和进程选择。在更新模型之前将标志设置为 true 并在更新后将其重置。
当用户点击 VK_ENTER 进入可编辑的 JComboBox 时,我正在尝试通过查找 Google Geocoder API 来实现自定义 JComboBox。
这里是代码:
package lucasepe.desktop.arsenal.widgets;
import java.awt.Component;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.SwingConstants;
import javax.swing.SwingWorker;
import lucasepe.desktop.arsenal.models.Address;
import lucasepe.desktop.arsenal.models.Location;
import lucasepe.desktop.arsenal.utils.IOUtils;
import lucasepe.desktop.enroll.utils.UIBundle;
import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONObject;
@SuppressWarnings("serial")
public class GeocoderComboBox extends JComboBox<Location> {
static final
private Logger LOG = Logger.getLogger(GeocoderComboBox.class);
private DefaultComboBoxModel<Location> mAdapter;
public GeocoderComboBox() {
super();
mAdapter = new DefaultComboBoxModel<Location>();
setModel(mAdapter);
setEditable(true);
getEditor().getEditorComponent()
.addKeyListener(new LocationSearcher());
setRenderer(new LocationListCellRenderer());
}
private class LocationSearcher extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
String constraint = ((JTextField)getEditor().getEditorComponent()).getText();
GeocodeWorker worker = new GeocodeWorker(constraint);
worker.execute();
}
}
};
private class GeocodeWorker extends SwingWorker<List<Location>, Void> {
final
private String mConstraint;
public GeocodeWorker(String constraint) {
mConstraint = (constraint != null) ?
constraint.toString().trim() : null;
}
@Override
protected List<Location> doInBackground() throws Exception {
if (mConstraint == null || mConstraint.length() == 0)
return null;
StringBuilder url = new StringBuilder();
url.append("https://maps.googleapis.com/maps/api/geocode/json?");
url.append("language=it");
url.append("&components=")
.append(encode("country:IT|route:")).append(encode(mConstraint));
url.append("&key=").append(UIBundle.getString("google_api_key"));
ArrayList<Location> locations = new ArrayList<Location>(10);
try {
JSONObject response = IOUtils.fetchJSONObject(url.toString());
if (response != null && response.optString("status", "KO").equals("OK")) {
JSONArray ja = response.getJSONArray("results");
for (int i = 0; i < ja.length(); i++) {
JSONObject jo = ja.getJSONObject(i);
String address = jo.optString("formatted_address");
if ((address != null) && address.trim().length() > 0) {
Address poi = new Address("GOOGLE");
LOG.debug("jo: " + jo.toString());
poi.setTitle(address);
poi.setCity(getShortName("locality", jo));
JSONObject geo = jo.getJSONObject("geometry")
.getJSONObject("location");
poi.setLatitude(geo.getDouble("lat"));
poi.setLongitude(geo.getDouble("lng"));
locations.add(poi);
}
}
}
} catch (Exception err) {
LOG.error("error: " + err.getMessage(), err);
}
return locations;
}
@Override
protected void done() {
try {
List<Location> data = get();
if (data == null || data.size() == 0)
mAdapter.removeAllElements();
else {
for (Location l: data)
mAdapter.addElement(l);
}
} catch (Exception err) {
LOG.error("error: " + err.getMessage(), err);
}
}
private String getShortName(String type, JSONObject jo) {
if (type == null || type.length() == 0 || jo == null)
return null;
String result = null;
try{
JSONArray ja = jo.optJSONArray("types");
if (ja != null) {
for (int i = 0; i < ja.length(); i++) {
if (ja.getString(i).equals(type)) {
result = jo.optString("short_name");
break;
}
}
}
} catch (Exception err) {
LOG.error("err: " + err.getMessage());
}
return result;
}
private String encode(String value) {
try {
return URLEncoder.encode(value, "UTF-8");
} catch (UnsupportedEncodingException err) {
LOG.error("encoding: " + value, err);
return value;
}
}
};
private class LocationListCellRenderer extends JPanel
implements
ListCellRenderer<Location> {
private JLabel mDisplayName;
public LocationListCellRenderer() {
super();
setOpaque(true);
setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
setLayout(new VerticalFlowLayout());
mDisplayName = new JLabel();
mDisplayName.setHorizontalAlignment(SwingConstants.LEFT);
add(mDisplayName);
}
@Override
public Component getListCellRendererComponent(
JList<? extends Location> list, Location value, int index,
boolean isSelected, boolean cellHasFocus) {
mDisplayName.setText(value.toString());
return this;
}
};
}
我是这样使用它的:
package lucasepe.desktop.enroll.fragments;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerDateModel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import lucasepe.desktop.arsenal.models.Address;
import lucasepe.desktop.arsenal.models.Location;
import lucasepe.desktop.arsenal.widgets.GeocoderComboBox;
import lucasepe.desktop.enroll.database.EnrollContract.PersonaColumns;
import net.java.dev.designgridlayout.DesignGridLayout;
import net.java.dev.designgridlayout.LabelAlignment;
import org.apache.log4j.PropertyConfigurator;
@SuppressWarnings("serial")
public class EditPersonaFragment extends JPanel {
private JTextField mFirstName;
private JTextField mLastName;
private JTextField mPhone;
private JTextField mEmail;
private JSpinner mBirthDate;
private JComboBox<PersonaColumns.Gender> mGender;
//private JTextField mAddress;
private GeocoderComboBox mAddress;
private JTextField mPostalCode;
private JTextField mCity;
private JTextField mState;
private JTextField mCountry;
public EditPersonaFragment() {
super();
mFirstName = new JTextField();
mLastName = new JTextField();
mPhone = new JTextField();
mEmail = new JTextField();
mBirthDate = new JSpinner();
mBirthDate.setModel(new SpinnerDateModel());
mBirthDate.setEditor(new JSpinner.DateEditor(mBirthDate, "dd/MM/yyyy"));
mGender = new JComboBox<PersonaColumns.Gender>(PersonaColumns.Gender.values());
//mAddress = new JTextField();
mAddress = new GeocoderComboBox();
mAddress.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent event) {
if (event.getStateChange() == ItemEvent.SELECTED) {
Address item = (Address)mAddress.getSelectedItem();
mCity.setText(item.getCity());
mState.setText(item.getState());
mCountry.setText(item.getCountry());
}
}
});
mCity = new JTextField();;
mState = new JTextField();
mCountry = new JTextField();
mPostalCode = new JTextField();
DesignGridLayout layout = new DesignGridLayout(this);
layout.labelAlignment(LabelAlignment.RIGHT);
layout.row()
.grid(new JLabel("Last Name")).add(mLastName)
.grid(new JLabel("First Name")).add(mFirstName);
layout.row()
.grid(new JLabel("Phone")).add(mPhone)
.grid(new JLabel("Email")).add(mEmail);
layout.row()
.grid(new JLabel("Birthdate")).add(mBirthDate)
.grid(new JLabel("Gender")).add(mGender);
layout.emptyRow();layout.emptyRow();
layout.row().center().fill().add(new JSeparator());
layout.emptyRow();layout.emptyRow();
layout.row().grid(new JLabel("Address")).add(mAddress);
layout.row()
.grid(new JLabel("City")).add(mCity)
.grid(new JLabel("Postal Code")).add(mPostalCode);
layout.row()
.grid(new JLabel("State")).add(mState)
.grid(new JLabel("Country")).add(mCountry);
}
public static void main(String[] args) throws Exception {
PropertyConfigurator.configure("logger.properties");
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("Login Form");
frame.getContentPane().add(new EditPersonaFragment());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
});
}
}
我第一次输入地址并按回车键时,Geocoder 工作但出现此异常:
java.lang.String cannot be cast to lucasepe.desktop.arsenal.models.Address
at lucasepe.desktop.enroll.fragments.EditPersonaFragment.itemStateChanged(EditPersonaFragment.java:66)
ItemListener 似乎已被触发:"How avoid this?"
当用户在可编辑的 JComboBox 上按下回车键时,是否有更好的方法来触发地理编码器?
我在想我做错了,也许有人会很友好地指出我正确的方向。
P.S.
我是一个 Android 愿意学习桌面方式的人,所以请不要打我太重:-)
正如我看到的那样,在工作人员的工作完成后,您从组合框模型中删除了所有选项并添加了结果。
您应该跳过侦听器中的事件,方法是在模型更新之前删除侦听器并在更新之后读取侦听器,或者引入一个标志(默认情况下我们称之为 isAPI)=false。在您的侦听器中,您只能在 isAPI=false 时检查标志和进程选择。在更新模型之前将标志设置为 true 并在更新后将其重置。