如何使JScrollPane滚动以跟随输入焦点?

我有一个带有大面板的Swing应用程序,它包含在JScrollPane 。 用户通常通过Tab键在面板的子组件之间移动,因此当他们选择查看某些内容时,我希望滚动窗格自动滚动,以便具有输入焦点的组件始终可见。

我已经尝试使用KeyboardFocusManager来监听输入焦点的变化,然后调用scrollRectToVisible

这是一个显示我当前策略的SSCCE (只需复制/粘贴并运行!):

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

如果你运行这个例子,你会发现它不能很好地工作。 它确实获得焦点更改通知,但对scrollRectToVisible的调用似乎没有任何效果。 在我的应用程序中(这里显示起来太复杂了),当我在视口外部选择某些内容时, scrollRectToVisible大约有一半的时间。

是否有既定的方法来解决这个问题? 如果它有任何区别,Swing应用程序是基于Netbeans RCP构建的(我们的大多数客户都运行Windows)。

我对其他答案的评论:

组件本身的scrollRectToVisible是该方法的全部要点;-)它被传递到层次结构,直到找到执行滚动的父级

…除非组件本身处理它 – 正如JTextField所做的那样:它实现为水平滚动以使插入符号可见。 解决方法是在字段的父级上调用方法。

编辑

为了清楚起见,更换的线是

  content.scrollRectToVisible(focused.getBounds()); 

你必须从JPanelJViewPort获取Rectangle ,然后比较,例如

通知(反对向下投票)最终和良好的输出需要一些工作在JViewPort中的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); } }); } } 

您的代码中的一个主要问题是:

 focused.scrollRectToVisible(focused.getBounds()); 

您正在调用组件本身的scrollRectToVisible! 大概是一个错字。 使您的JScrollPane成为最终变量并调用

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

这里jtextbox是你要关注的组件,jscrollpane是你的滚动窗格:

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