2011-11-23 57 views
9

Tengo una aplicación Swing con un panel grande que está envuelto en un JScrollPane. Los usuarios normalmente se mueven entre los subcomponentes del panel mediante pestañas, de modo que cuando seleccionan un tabulador para ver algo, quiero que el panel de desplazamiento se desplace automáticamente para que el componente con enfoque de entrada esté siempre visible.¿Cómo hago que JScrollPane scroll siga el enfoque de entrada?

He intentado usar KeyboardFocusManager para escuchar cambios en el foco de entrada, y luego llamar al scrollRectToVisible.

He aquí un SSCCE mostrar mi estrategia actual (sólo tienes que copiar/pegar y correr!):

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); 
     } 
    }); 
    } 
} 

Si ejecuta este ejemplo, se dará cuenta de que no funciona muy bien. Recibe las notificaciones de cambio de foco, pero la llamada a scrollRectToVisible no parece tener ningún efecto. En mi aplicación (que es demasiado compleja para mostrar aquí), scrollRectToVisible funciona la mitad del tiempo cuando tabulo en algo fuera de la ventana gráfica.

¿Existe alguna manera establecida de resolver este problema? Si hace alguna diferencia, la aplicación Swing se basa en Netbeans RCP (y la mayoría de nuestros clientes ejecutan Windows).

Respuesta

13

Mi comentario a la otra respuesta:

scrollRectToVisible en el componente en sí es todo el punto de que método ;-) Se transmite por la jerarquía hasta que un padre haciendo el rollo se encuentra

... excepto cuando el propio componente se encarga de ello - como lo hace JTextField: se implementa para desplazarse horizo ntally para hacer el caret visible. La salida es llamar al método en el padre del campo.

Editar

sólo por la claridad, la línea reemplazado es

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

Gracias por la explicación. –

+5

Sí, gracias por la explicación. La solución propuesta no funcionaba en mi aplicación (aunque funcionaba como un encanto en el ejemplo anterior), pero después de un poco de experimentación, encontré que lo siguiente funcionaba: '((JComponent) focused.getParent()). ScrollRectToVisible (focused.getBounds()) 'Ahora, nadie respira o el seguidor de enfoque se rompe ...;) – gustafc

0

Una cuestión importante en su código es:

focused.scrollRectToVisible(focused.getBounds()); 

Usted está llamando scrollRectToVisible en el propio componente! Presumiblemente un error tipográfico. Haga su JScrollPane una variable final y pedir

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

scrollRectToVisible en el propio componente es el point_ _whole de ese método ;-) Se transmite por la jerarquía hasta que un padre haciendo el desplazamiento se encuentra – kleopatra

+0

@kleopatra Yo no sabía eso.El cambio que propuse funciona bien, pero no tengo idea de por qué no funciona como es. –

+0

ver mi respuesta en cuanto al por qué (notó el tipo de componente solo después de publicar el comentario) Sin embargo, _do no_ mensaje a la ventana directamente por la cordura del código – kleopatra

3

usted tiene que tomar Rectangle de JPanel y JViewPort también, a continuación, comparar, por ejemplo

notificación (en contra de abajo -voting) para una salida final y agradable requirió algún trabajo para las posiciones en el 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, no necesita a) ninguna manipulación de rectángulo adicional b) campo final – kleopatra

+0

tiene que retirarse un poco en b) - la referencia a el contenido no es necesario para desplazarse, es necesario para verificar si el enfoque está en la jerarquía secundaria del contenido – kleopatra

0

Aquí jtextbox es el componente que desea enfocar y JScrollPane es su panel de desplazamiento:

jScrollpane.getVerticalScrollBar().setValue(jtextbox.getLocation().x); 
Cuestiones relacionadas