如何使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());
你必须从JPanel
和JViewPort
获取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);