在 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 并在更新后将其重置。