Search code examples
javaswingautocompletefocusjtextfield

JTextField focus is not getting on the first click of Tab button


i've done an application with two JTextField with the autocomplete feature the application works fine but On clicking the Tab Button while the mouse pointer is on the first TextField the Mouse focus is not going into the second Textfield but gets the focus on the second time. i.e for passing the focus we have press the Tab button twice

Can anyone tell me wat's the reason and the solution for this problem

My code is given below

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.*;
import java.text.Collator;
import java.util.*;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;

public class AutoCompleteExample extends javax.swing.JFrame {
    public AutoCompleteExample() {
        initComponents();
         List<Country> countries = new ArrayList<Country>();

        Locale[] locales = Locale.getAvailableLocales();
        for (Locale locale : locales) {
        String iso = locale.getISO3Country();
        String code = locale.getCountry();

        String name = locale.getDisplayCountry();

        if (!"".equals(iso) && !"".equals(code) && !"".equals(name)) {
            countries.add(new Country(iso, code, name));
        }
        }

        Collections.sort(countries, new CountryComparator());
        java.util.Set<String> item = new java.util.TreeSet<String>();
        for(Country cn:countries){
            item.add(""+cn);
        }
        ArrayList<String> items = new ArrayList<String>(item);
        setupAutoComplete(country,code, items);
        country.setColumns(30);   
    }
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        jDesktopPane1 = new javax.swing.JDesktopPane();
        jLabel1 = new javax.swing.JLabel();
        country = new javax.swing.JTextField();
        jLabel2 = new javax.swing.JLabel();
        code = new javax.swing.JTextField();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jDesktopPane1.setBackground(javax.swing.UIManager.getDefaults().getColor("Button.background"));

        jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        jLabel1.setText("Country Name");
        jLabel1.setBounds(0, 10, 80, 20);
        jDesktopPane1.add(jLabel1, javax.swing.JLayeredPane.DEFAULT_LAYER);

        country.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusLost(java.awt.event.FocusEvent evt) {
                countryFocusLost(evt);
            }
        });
        country.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyReleased(java.awt.event.KeyEvent evt) {
                countryKeyReleased(evt);
            }
        });
        country.setBounds(90, 10, 140, 20);
        jDesktopPane1.add(country, javax.swing.JLayeredPane.DEFAULT_LAYER);

        jLabel2.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        jLabel2.setText("Code");
        jLabel2.setBounds(235, 10, 50, 20);
        jDesktopPane1.add(jLabel2, javax.swing.JLayeredPane.DEFAULT_LAYER);
        code.setBounds(300, 10, 160, 20);
        jDesktopPane1.add(code, javax.swing.JLayeredPane.DEFAULT_LAYER);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jDesktopPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 473, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jDesktopPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 43, Short.MAX_VALUE)
        );

        pack();
    }// </editor-fold>

    private void countryFocusLost(java.awt.event.FocusEvent evt) {
        // TODO add your handling code here:
    }

    private void countryKeyReleased(java.awt.event.KeyEvent evt) {

    }

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                new AutoCompleteExample().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify
    public static javax.swing.JTextField code;
    public static javax.swing.JTextField country;
    private javax.swing.JDesktopPane jDesktopPane1;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    // End of variables declaration



 private static boolean isAdjusting(JComboBox cbInput) {
        if (cbInput.getClientProperty("is_adjusting") instanceof Boolean) {
            return (Boolean) cbInput.getClientProperty("is_adjusting");
        }
        return false;
    }

    private static void setAdjusting(JComboBox cbInput, boolean adjusting) {
        cbInput.putClientProperty("is_adjusting", adjusting);
    }

    public static void setupAutoComplete(final JTextField txtInput,final JTextField txtInput2, final ArrayList<String> items) {
        final DefaultComboBoxModel model = new DefaultComboBoxModel();
        final JComboBox cbInput = new JComboBox(model) {
            public Dimension getPreferredSize() {
                return new Dimension(super.getPreferredSize().width, 0);
            }
        };
        setAdjusting(cbInput, false);
        for (Object item : items) {
            model.addElement(item);
        }
        cbInput.setSelectedItem(null);
        cbInput.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (!isAdjusting(cbInput)) {
                    if (cbInput.getSelectedItem() != null) {
                        txtInput.setText(cbInput.getSelectedItem().toString());
                        txtInput2.setText(datas.get(cbInput.getSelectedItem().toString())+"");
                    }
                }
            }
        });

        txtInput.addKeyListener(new KeyAdapter() {

            @Override
            public void keyPressed(KeyEvent e) {
                setAdjusting(cbInput, true);
                if (e.getKeyCode() == KeyEvent.VK_SPACE) {
                    if (cbInput.isPopupVisible()) {
                        e.setKeyCode(KeyEvent.VK_ENTER);
                    }
                }
                if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN) {
                    e.setSource(cbInput);
                    cbInput.dispatchEvent(e);
                    if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                        txtInput.setText(cbInput.getSelectedItem().toString());
                        // System.out.println("SEEEEE:"+datas.get(cbInput.getSelectedItem().toString()));
                        txtInput2.setText(datas.get(cbInput.getSelectedItem().toString())+"");
                        cbInput.setPopupVisible(false);
                    }
                }
                if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                    cbInput.setPopupVisible(false);
                }
                setAdjusting(cbInput, false);
            }
            @Override
             public void keyReleased(KeyEvent e)
             {
                setAdjusting(cbInput, true);
                if (e.getKeyCode() == KeyEvent.VK_SPACE) {
                    if (cbInput.isPopupVisible()) {
                        e.setKeyCode(KeyEvent.VK_ENTER);
                    }
                }
                if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN) {
                    e.setSource(cbInput);
                    cbInput.dispatchEvent(e);
                    if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                        txtInput.setText(cbInput.getSelectedItem().toString());
                        // System.out.println("SEEEEE:"+datas.get(cbInput.getSelectedItem().toString()));
                        txtInput2.setText(datas.get(cbInput.getSelectedItem().toString())+"");
                        cbInput.setPopupVisible(false);
                    }
                }
                if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                    cbInput.setPopupVisible(false);
                }
                setAdjusting(cbInput, false);
             }
        });


        txtInput.addFocusListener(new FocusListener() {
            public void focusGained(FocusEvent e) {

            }
            @Override
            public void focusLost(FocusEvent e) {
            try{     setAdjusting(cbInput, true);
                 e.setSource(cbInput);
                 cbInput.dispatchEvent(e);
                 txtInput.setText(cbInput.getSelectedItem().toString());
                       // System.out.println("SEEEEE:"+datas.get(cbInput.getSelectedItem().toString()));
                 txtInput2.setText(datas.get(cbInput.getSelectedItem().toString())+"");
                 cbInput.setPopupVisible(false); 
                 setAdjusting(cbInput, false);
            }catch(Exception s){country.setText("");code.setText("");
            }
              }
            });


        txtInput.getDocument().addDocumentListener(new DocumentListener() {
            public void insertUpdate(DocumentEvent e) {
                updateList();
            }

            public void removeUpdate(DocumentEvent e) {
                updateList();
            }

            public void changedUpdate(DocumentEvent e) {
                updateList();
            }

            private void updateList() {
                setAdjusting(cbInput, true);
                model.removeAllElements();
                String input = txtInput.getText();
                if (!input.isEmpty()) {
                    for (Object item : items) {
                        if ((item+"").toLowerCase().startsWith(input.toLowerCase())) {
                            model.addElement(item);
                        }
                    }
                }
                cbInput.setPopupVisible(model.getSize() > 0);
                setAdjusting(cbInput, false);
            }
        });
        txtInput.setLayout(new BorderLayout());
        txtInput.add(cbInput, BorderLayout.SOUTH);
    }
    public static HashMap datas=new HashMap();;
}

////////////////////////////////////////////////////class JTextFieldLimits for limiting texfield strength/////////////////////////////////////////

class JTextFieldLimits extends PlainDocument {
  private int limit;
  JTextFieldLimits(int limit) {
   super();
   this.limit = limit;
   }

    @Override
  public void insertString( int offset, String  str, AttributeSet attr ) throws BadLocationException {
    if (str == null) return;

    if ((getLength() + str.length()) <= limit) {
      super.insertString(offset, str, attr);
    }
  }
}

////////////////////////////////////////////////////class Country for getting names/////////////////////////////////////////


class Country {
  private String iso;

  private String code;

  public String name;

  Country(String iso, String code, String name) {
    this.iso = iso;
    this.code = code;
    this.name = name;
      AutoCompleteExample.datas.put(name, code);
  }

    @Override
  public String toString() {

    return  name;
  }

  public String getName() {
    return  code;
  }



}

class CountryComparator implements Comparator<Country> {
  private Comparator comparator;

  CountryComparator() {
    comparator = Collator.getInstance();
  }

    @Override
  public int compare(Country o1, Country o2) {
    return comparator.compare(o1.name, o2.name);
  }
}

Thanks in advance.


Solution

  • The problem is that your JComboBox you're using for displaying the autocomplete is focusable. So when you hit tab, it focuses on the non-visible ComboBox. If you use this line after declaring cbInput in setupAutoComplete, it should fix your problem:

        cbInput.setFocusable(false);
    

    Example:

    public static void setupAutoComplete(final JTextField txtInput,final JTextField txtInput2, final ArrayList<String> items) {
            final DefaultComboBoxModel model = new DefaultComboBoxModel();
            final JComboBox cbInput = new JComboBox(model) {
                public Dimension getPreferredSize() {
                    return new Dimension(super.getPreferredSize().width, 0);
                }
            };
    
            //The important line:
            cbInput.setFocusable(false);
    
            setAdjusting(cbInput, false);
            for (Object item : items) {
                model.addElement(item);
            }
            ...
    }