2012-04-05 11 views
10

Ho uno JTextField per il quale spero di suggerire risultati che corrispondano all'input dell'utente. Sto visualizzando questi suggerimenti in un JList contenuto in un JPopupMenu.Arresto del JPopupMenu che ruba lo stato attivo

Tuttavia, quando si apre il menu a comparsa di programmazione tramite show(Component invoker, int x, int y), l'attenzione è sempre preso dal JTextField.

Stranamente, se invece chiamo setVisible(true), lo stato attivo non viene rubato; ma il JPopupMenu non è collegato a nessun pannello e, quando si riduce a icona l'applicazione mentre la scatola è aperta, rimane dipinta sulla finestra.

Ho anche cercato di ripristinare lo stato attivo al JTextField utilizzando requestFocus(), ma poi ho dovuto ripristinare la posizione del punto di inserimento utilizzando SwingUtilities.invokeLater(), e il invoke tardi lato delle cose è dare all'utente un leggero margine di pasticciare con i contenuti esistenti/sovrascrivi/o fai altre cose imprevedibili.

Il codice che ho è efficace:

JTextField field = new JTextField(); 
JPopupMenu menu = new JPopupMenu(); 

field.addKeyListener(new KeyAdapter() { 
    public void keyTyped(KeyEvent e) { 
     JList list = getAListOfResults(); 

     menu.add(list); 
     menu.show(field, 0, field.getHeight()); 
    } 
}); 

Qualcuno può suggerire la strada migliore per andare verso il basso per mostrare la programmazione JPopupMenu preservando l'attenzione sul JTextField?

risposta

14

La risposta tecnica è quello di impostare la proprietà attivabile del popup su false:

popup.setFocusable(false); 

L'implicazione è che il textField deve assumere tutte le azioni di tastiera e mouse-triggered che normalmente vengono gestiti dal lista stessa, il sosmething come:

final JList list = new JList(Locale.getAvailableLocales()); 
final JPopupMenu popup = new JPopupMenu(); 
popup.add(new JScrollPane(list)); 
popup.setFocusable(false); 
final JTextField field = new JTextField(20); 
Action down = new AbstractAction("nextElement") { 

    @Override 
    public void actionPerformed(ActionEvent e) { 
     int next = Math.min(list.getSelectedIndex() + 1, 
       list.getModel().getSize() - 1); 
     list.setSelectedIndex(next); 
     list.ensureIndexIsVisible(next); 
    } 
}; 
field.getActionMap().put("nextElement", down); 
field.getInputMap().put(
     KeyStroke.getKeyStroke("DOWN"), "nextElement"); 

come il vostro contesto è molto simile ad un JComboBox, si potrebbe considerare di avere uno sguardo nelle fonti di BasicComboBoxUI e Basi cComboPopup.

Modifica

Solo per divertimento, il seguente non rispondendo alla domanda di messa a fuoco :-) Invece è, esso dimostra come utilizzare un JXList ordinabile/filtrabile per visualizzare solo le opzioni nel menu a discesa che corrispondono al testo digitato (qui con un inizia-con regola)

// instantiate a sortable JXList 
final JXList list = new JXList(Locale.getAvailableLocales(), true); 
list.setSortOrder(SortOrder.ASCENDING); 

final JPopupMenu popup = new JPopupMenu(); 
popup.add(new JScrollPane(list)); 
popup.setFocusable(false); 
final JTextField field = new JTextField(20); 

// instantiate a PatternModel to map text --> pattern 
final PatternModel model = new PatternModel(); 
model.setMatchRule(PatternModel.MATCH_RULE_STARTSWITH); 
// listener which to update the list's RowFilter on changes to the model's pattern property 
PropertyChangeListener modelListener = new PropertyChangeListener() { 

    @Override 
    public void propertyChange(PropertyChangeEvent evt) { 
     if ("pattern".equals(evt.getPropertyName())) { 
      updateFilter((Pattern) evt.getNewValue()); 
     } 
    } 

    private void updateFilter(Pattern newValue) { 
     RowFilter<Object, Integer> filter = null; 
     if (newValue != null) { 
      filter = RowFilters.regexFilter(newValue); 
     } 
     list.setRowFilter(filter); 
    } 
}; 
model.addPropertyChangeListener(modelListener); 

// DocumentListener to update the model's rawtext property on changes to the field 
DocumentListener documentListener = new DocumentListener() { 

    @Override 
    public void removeUpdate(DocumentEvent e) { 
     updateAfterDocumentChange(); 
    } 

    @Override 
    public void insertUpdate(DocumentEvent e) { 
     updateAfterDocumentChange(); 
    } 

    private void updateAfterDocumentChange() { 
     if (!popup.isVisible()) { 
      popup.show(field, 0, field.getHeight()); 
     } 
     model.setRawText(field.getText()); 
    } 

    @Override 
    public void changedUpdate(DocumentEvent e) { 
    } 
}; 
field.getDocument().addDocumentListener(documentListener); 
+0

Ottima risposta ... funziona perfettamente .. grazie;) – Isaac

+0

Questo mi ha aiutato molto, grazie! – Epaga

+0

Dopo molte ricerche su google, questa risposta mi ha aiutato molto –

2

Mi sembra semplice. Aggiungere il seguente

field.requestFocus();

dopo

menu.add(list); 
menu.show(field, 0, field.getHeight()); 

Naturalmente, si dovrà codificare per quando per nascondere l'etc comparsa sulla base di ciò che sta accadendo con l'JTextField.

i.e;

menu.show(field, field.getX(), field.getY()+field.getHeight()); 
menu.setVisible(true); 
field.requestFocus(); 
+0

Grazie per la tua risposta; sfortunatamente ho già provato questo approccio. Diventa più complicato poiché devo ripristinare la posizione del cursore usando 'SwingUtilities.invokeLater()', e il * richiama più tardi * le cose dà all'utente un leggero margine per manipolare i contenuti esistenti/sovrascriverli/o fare altre cose imprevedibili. – Isaac

Problemi correlati