2011-11-23 16 views
9

Ho un'app Swing con un pannello grande che è avvolto in un JScrollPane. Normalmente gli utenti si spostano tra i sottocomponenti del pannello tramite tabulazione, quindi quando si sfogliano per visualizzare qualcosa fuori, voglio che il riquadro di scorrimento scorri automaticamente in modo che il componente con lo stato attivo sia sempre visibile.Come faccio a far scorrere JScrollPane per seguire lo stato attivo di input?

Ho provato con KeyboardFocusManager ad ascoltare per le modifiche di messa a fuoco di ingresso, e quindi chiamando scrollRectToVisible.

Ecco un SSCCE visualizzazione mia strategia corrente (solo copia/incolla e correre!):

import java.awt.KeyboardFocusManager; 
import java.beans.PropertyChangeEvent; 
import java.beans.PropertyChangeListener; 
import javax.swing.*; 

public class FollowFocus { 

    public static void main(String[] args) { 
    SwingUtilities.invokeLater(new Runnable() { 

     public void run() { 
     final int ROWS = 100; 
     final JPanel content = new JPanel(); 
     content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS)); 
     content.add(new JLabel(
      "Thanks for helping out. Use tab to move around.")); 
     for (int i = 0; i < ROWS; i++) { 
      JTextField field = new JTextField("" + i); 
      field.setName("field#" + i); 
      content.add(field); 
     } 

     KeyboardFocusManager.getCurrentKeyboardFocusManager() 
          .addPropertyChangeListener("focusOwner", 
        new PropertyChangeListener() { 

      @Override 
      public void propertyChange(PropertyChangeEvent evt) { 
      if (!(evt.getNewValue() instanceof JComponent)) { 
       return; 
      } 
      JComponent focused = (JComponent) evt.getNewValue(); 
      if (content.isAncestorOf(focused)) { 
       System.out.println("Scrolling to " + focused.getName()); 
       focused.scrollRectToVisible(focused.getBounds()); 
      } 
      } 
     }); 

     JFrame window = new JFrame("Follow focus"); 
     window.setContentPane(new JScrollPane(content)); 
     window.setSize(200, 200); 
     window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     window.setVisible(true); 
     } 
    }); 
    } 
} 

Se si esegue questo esempio, si noterà che non funziona molto bene. Ottiene le notifiche di cambio di messa a fuoco, ma la chiamata a scrollRectToVisible non sembra avere alcun effetto. Nella mia app (che è troppo complessa da mostrare qui), scrollRectToVisible funziona all'incirca alla metà del tempo in cui inserisco una scheda al di fuori del viewport.

Esiste un modo consolidato per risolvere questo problema? Se fa alcuna differenza, l'app Swing è costruita su Netbeans RCP (e la maggior parte dei nostri clienti esegue Windows).

risposta

13

commenti per il altra risposta:

scrollRectToVisible sul componente stesso è il punto centrale di quel metodo ;-) E 'passata la gerarchia fino a quando un genitore fa il di scorrimento si trova

... tranne se il componente stesso gestisce - come fa JTextField: è implementato per scorrere horizo per rendere visibile il punto di vista. La via d'uscita è chiamare il metodo sul genitore del campo.

Modifica

solo per chiarezza, la linea sostituito è

content.scrollRectToVisible(focused.getBounds()); 
+0

Grazie per la spiegazione. –

+5

Sì, grazie per la spiegazione. La tua proposta di correzione non funzionava nella mia app (anche se ha funzionato come un fascino nell'esempio sopra), ma dopo un po 'di sperimentazione ho trovato il seguente lavoro: '((JComponent) focused.getParent()). ScrollRectToVisible (focused.getBounds()) 'Ora, nessuno respira o l'inseguitore di messa a fuoco si rompe ...;) – gustafc

0

Una questione importante nel codice è:

focused.scrollRectToVisible(focused.getBounds()); 

Si chiama scrollRectToVisible sul componente stesso! Presumibilmente un errore di battitura. Fai la tua JScrollPane una variabile finale e chiedere

scrollPane.getViewport().scrollRectToVisible(focused.getBounds()); 
+0

scrollRectToVisible sul componente stesso è il point_ _whole di tale metodo ;-) E 'passata la gerarchia fino a quando un genitore facendo lo scroll si trova – kleopatra

+0

@kleopatra Non lo sapevo.Il cambiamento che ho proposto funziona bene, ma non ho idea del perché non funziona così com'è. –

+0

vedere la mia risposta al perché (ho notato il tipo di componente solo dopo aver postato il commento) Tuttavia, _do not_ messaggio direttamente sul viewport per il codice sanità – kleopatra

3

si deve prendere Rectangle da JPanel e JViewPort troppo, quindi confrontare, per esempio

preavviso (contro giù -voting) per l'output finale e piacevole richiesto un po 'di lavoro per le posizioni nel JViewPort

import java.awt.KeyboardFocusManager; 
import java.awt.Rectangle; 
import java.beans.PropertyChangeEvent; 
import java.beans.PropertyChangeListener; 
import javax.swing.*; 
//http://stackoverflow.com/questions/8245328/how-do-i-make-jscrollpane-scroll-to-follow-input-focus 
public class FollowFocus { 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 

      public void run() { 
       final int ROWS = 100; 
       final JPanel content = new JPanel(); 
       content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS)); 
       content.add(new JLabel(
         "Thanks for helping out. Use tab to move around.")); 
       for (int i = 0; i < ROWS; i++) { 
        JTextField field = new JTextField("" + i); 
        field.setName("field#" + i); 
        content.add(field); 
       } 
       final JScrollPane scroll = new JScrollPane(content); 
       KeyboardFocusManager.getCurrentKeyboardFocusManager(). 
         addPropertyChangeListener("focusOwner", new PropertyChangeListener() { 

        @Override 
        public void propertyChange(PropertyChangeEvent evt) { 
         if (!(evt.getNewValue() instanceof JComponent)) { 
          return; 
         } 
         JViewport viewport = (JViewport) content.getParent(); 
         JComponent focused = (JComponent) evt.getNewValue(); 
         if (content.isAncestorOf(focused)) { 
          System.out.println("Scrolling to " + focused.getName()); 
          Rectangle rect = focused.getBounds(); 
          Rectangle r2 = viewport.getVisibleRect(); 
          content.scrollRectToVisible(new Rectangle(rect.x, rect.y, (int) r2.getWidth(), (int) r2.getHeight())); 
         } 
        } 
       }); 

       JFrame window = new JFrame("Follow focus"); 
       window.setContentPane(new JScrollPane(content)); 
       window.setSize(200, 200); 
       window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       window.setVisible(true); 
      } 
     }); 
    } 
} 
+0

no, non è necessario a) nessuna manipolazione del rettangolo aggiuntivo b) il campo finale – kleopatra

+0

deve ritirarsi un po 'su b) - il riferimento a il contenuto non è necessario per lo scorrimento, è necessario per verificare se il focus è nella gerarchia figlio del contenuto – kleopatra

0

Ecco jtextbox è il componente che si desidera mettere a fuoco e JScrollPane è il vostro ScrollPane:

jScrollpane.getVerticalScrollBar().setValue(jtextbox.getLocation().x); 
Problemi correlati