2011-11-23 23 views
9

我有一个包含在JScrollPane中的大面板的Swing应用程序。用户通常通过制表​​符在面板的子组件之间移动,因此当他们选项卡到某个视图外时,我希望滚动窗格自动滚动,以便始终可以看到具有输入焦点的组件。如何让JScrollPane滚动来关注输入焦点?

我试过使用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)。

回答

13

我对对方的回答评论:

scrollRectToVisible的组件本身是 方法;-)整点它向上传递层次直到父母做 滚动找到

...除了组件本身处理它时 - 作为JTextField做:它实现滚动horizo通常可以看到插入的字符。出路是调用该字段父级的方法。

编辑

只是为了清楚起见,更换线路

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

感谢您的解释。 –

+5

是的,谢谢你的解释。您提出的修复方法在我的应用中无效(尽管它在上面的例子中像一个魅力一样),但经过一些实验后,我发现以下工作:'((JComponent)focused.getParent())。scrollRectToVisible focused.getBounds())'现在,没有人呼吸或焦点追随者打破...;) – gustafc

0

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

focused.scrollRectToVisible(focused.getBounds()); 

您的组件本身调用scrollRectToVisible!大概是一个错字。 让你JScrollPane的最终的变数和呼叫

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

scrollRectToVisible的组件本身就是它的向上传递的层次结构,直到父母该方法;-)的_whole point_做滚动发现 – kleopatra

+0

@kleopatra我不知道这一点。我提议的改变工作正常,但我不知道为什么它不能正常工作。 –

+0

看到我的答案为什么(发布注释后才注意到组件的类型)尽管如此,_do not_直接为代码健全发送视口 – kleopatra

3

你必须采取从JPanelJViewPortRectangle过,然后进行比较,例如

通知(反降 - 投票)为最后和很好的输出需要一些工作的位置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

不,你不需要a)任何额外的矩形操作b)最后的字段 – kleopatra

+0

必须在b)上退缩一点 - 内容不需要滚动,它需要检查焦点是否在内容的子级别中 – kleopatra

0

这里jtextbox是要聚焦和JScrollPane的是你的滚动面板组件:

jScrollpane.getVerticalScrollBar().setValue(jtextbox.getLocation().x); 
相关问题