【问题标题】:How to make Scrollbar of scroll pane make movable automatically with the cursor as we press TAB Button on keyboard?当我们按下键盘上的 TAB 按钮时,如何使滚动窗格的滚动条随光标自动移动?
【发布时间】:2011-11-05 20:19:17
【问题描述】:

我有以下代码。执行以下代码后,它显示了一个包含一个 JInternalFrame 的 JFrame,它有一个 JScrollPane 这个 JScrollPane 有一个 JPanel 有很多输入控件。由于 JPanel 的大小由于许多输入控件而比 JInternalFrame 的大小要大,所以我它是可滚动的。

import java.awt.Dimension;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;

public class InterfaceDesign {

    public static void main(String args[]) {
        InterfaceDesign id = new InterfaceDesign();
        id.getPnlProjectDetail("My Project");
    }

    public void getPnlProjectDetail(String strProjectName) {
        JPanel pnlPjDetail = new JPanel();
        JScrollPane scrPjDetail;
        pnlPjDetail.setLayout(null);
        pnlPjDetail.setBounds(0, 0, 400, 400);
        JFrame frmtest = new JFrame();
        JInternalFrame interFrame = new JInternalFrame();
        interFrame.setBounds(0, 0, 280, 180);

        frmtest.setBounds(1, 1, 300, 200);
        pnlPjDetail.setPreferredSize(new Dimension(400, 400));

        JLabel lblFirstName = new JLabel("First Name");
        JLabel lblLastName = new JLabel("Last Name");
        JLabel lblAddress = new JLabel("Address");
        JLabel lblCity = new JLabel("City");
        JLabel lblZipCode = new JLabel("Zip Code");
        JLabel lblPhone = new JLabel("Phone");
        JLabel lblEmailID = new JLabel("Emain Id");

        JTextField tfFirstName = new JTextField();
        JTextField tfLastName = new JTextField();
        JTextField tfAddress = new JTextField();
        JTextField tfCity = new JTextField();
        JTextField tfZipCode = new JTextField();
        JTextField tfPhone = new JTextField();
        JTextField tfEmailID = new JTextField();

        lblFirstName.setBounds(25, 55, 85, 20);
        tfFirstName.setBounds(25, 85, 85, 20);
        pnlPjDetail.add(lblFirstName);
        pnlPjDetail.add(tfFirstName);

        lblLastName.setBounds(25, 115, 85, 20);
        tfLastName.setBounds(25, 145, 85, 20);
        pnlPjDetail.add(lblLastName);
        pnlPjDetail.add(tfLastName);

        lblAddress.setBounds(25, 175, 85, 20);
        tfAddress.setBounds(25, 205, 85, 20);
        pnlPjDetail.add(lblAddress);
        pnlPjDetail.add(tfAddress);

        lblCity.setBounds(25, 235, 85, 20);
        tfCity.setBounds(25, 265, 85, 20);
        pnlPjDetail.add(lblCity);
        pnlPjDetail.add(tfCity);

        lblZipCode.setBounds(25, 295, 85, 20);
        tfZipCode.setBounds(25, 325, 85, 20);
        pnlPjDetail.add(lblZipCode);
        pnlPjDetail.add(tfZipCode);

        lblPhone.setBounds(25, 355, 85, 20);
        tfPhone.setBounds(25, 385, 85, 20);
        pnlPjDetail.add(lblPhone);
        pnlPjDetail.add(tfPhone);

        lblEmailID.setBounds(25, 415, 85, 20);
        tfEmailID.setBounds(25, 445, 85, 20);
        pnlPjDetail.add(lblEmailID);
        pnlPjDetail.add(tfEmailID);

        scrPjDetail = new JScrollPane(pnlPjDetail);

        scrPjDetail.setAutoscrolls(true);

        //frmtest.setContentPane(scrProjectDetail);
        interFrame.setContentPane(scrPjDetail);
        JDesktopPane dpane = new JDesktopPane();
        interFrame.setVisible(true);
        dpane.add(interFrame);

        //frmtest.getLayeredPane().add(interFrame);
        frmtest.setContentPane(dpane);
        //frmtest.add(scrProjectDetail);
        frmtest.setVisible(true);
        frmtest.setResizable(false);
        frmtest.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //return pnlPjDetail;
    }
}

我需要您的指导来解决以下问题/疑问

  1. 当我们按下键盘上的 TAB 按钮时,光标会从一个文本框移动到另一个文本框,但 ScrollBar(此处为垂直滚动条)不会随着光标移动到下方输入字段而自动移动 那么如何使滚动条移动当它向下或向上移动时,它本身是否与光标一起移动?
  2. 由于这是一个演示代码,是否可以将两个 JscrollPane 并排添加到 JInterNalFrame 中?
  3. 是否需要用户 JDesktopPane 将 JInternalFrame 添加到 JFrame 中?即我们不能按如下方式添加 JInternalFrame 是真的吗 <JFrame>.getContentPane.add(<JInternalFrame>);

【问题讨论】:

  • 与您的问题无关,有几个 cmets:a)不要使用 null 布局 b)不要在应用程序代码中使用 setXXSize c)使用可读的字段/方法名称(没有类似匈牙利语的前缀)

标签: java swing jpanel scrollbar jscrollpane


【解决方案1】:

简单的任务,令人惊讶的古怪解决方案 - 就个人而言,我认为每个必须依赖焦点的应用程序代码解决方案都是古怪的;-)

无法提出任何不依赖于焦点的东西:通过不需要在表单上的每个组件上都使用 focusListener,只会稍微提高可维护性。相反,向 KeyboardFocusManager 注册一个 PropertyChangeListener 并在收到关于其 PermanentFocusOwner 属性更改的通知时进行滚动

public static class FocusDrivenScroller implements PropertyChangeListener {

    private JComponent parent;

    public FocusDrivenScroller(JComponent parent) {
        this.parent = parent;
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        Component focused = (Component) evt.getNewValue();
        if (focused == null || !SwingUtilities.isDescendingFrom(focused, parent)) return;
        parent.scrollRectToVisible(focused.getBounds());
    }

}

要在应用程序代码中使用,请使用 JScrollPane 中的表单进行实例化

public void buildAndShowDetailsFrame(String strProjectName) {
    // the container to scroll such that a focused child
    // is visible when gaining focus
    JPanel detailsForm = new JPanel();
    KeyboardFocusManager.getCurrentKeyboardFocusManager()
        .addPropertyChangeListener("permanentFocusOwner", 
                new FocusDrivenScroller(detailsForm));

    // choose and use an appropriate LayoutManager
    // note: this is only an example!
    // real-world requirements most probably need a stronger one
    detailsForm.setLayout(new BoxLayout(detailsForm, BoxLayout.PAGE_AXIS));
    // quick fill with stuff 
    String[] labels = {"First Name", "Last Name", 
            "Address", "City", "Zip Code", "Phone", "Emain Id"};
    for (String string : labels) {
        detailsForm.add(new JLabel(string));
        detailsForm.add(new JTextField());
    }
    JFrame frame = new JFrame();
    frame.add(new JScrollPane(detailsForm));
    frame.pack();
    // force scrollbar to appear
    frame.setSize(frame.getWidth(), frame.getHeight()/2);
    frame.setVisible(true);
    frame.setResizable(false);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

除了这个方法的第一行之外,它只是 OP 代码的缩短版本(用来引诱他/她使用适当的 LayoutManager :-)

【讨论】:

    【解决方案2】:

    Scrolling a Form 是对 Kleopartra 建议的精美实现。

    【讨论】:

      【解决方案3】:

      关于您的滚动问题:例如,您可以添加一个监听器来关注事件并相应地滚动您的窗格。

      FocusAdapter scrollFocusListener = new FocusAdapter() {
      @Override
          public void focusGained(FocusEvent e) {
              System.out.println(((JComponent) e.getSource()).getBounds());
              scrPjDetail.getViewport().scrollRectToVisible(((JComponent) e.getSource()).getBounds());
          }
      };
      tfFirstName.addFocusListener(scrollFocusListener);
      tfLastName.addFocusListener(scrollFocusListener);
      tfAddress.addFocusListener(scrollFocusListener);
      ...
      

      【讨论】:

      • 谢谢先生!对于您的解决方案,我已经实现了它。当我按下 TAB 按钮并且光标从上输入 filelds 移动到 downwords 输入字段时,它的工作原理。但是当我按 SHIFT+TAB 按钮将光标从当前输入字段移动到上一个输入字段时,滚动条不会随光标自动移动。可能是我错误地实施了您的解决方案,但请先生指导我!谢谢!
      【解决方案4】:

      试试这个:

      import java.awt.Component;
      import java.awt.Dimension;
      import java.awt.event.FocusEvent;
      import java.awt.event.FocusListener;
      
      import javax.swing.JComponent;
      import javax.swing.JDesktopPane;
      import javax.swing.JFrame;
      import javax.swing.JInternalFrame;
      import javax.swing.JLabel;
      import javax.swing.JPanel;
      import javax.swing.JScrollPane;
      import javax.swing.JSplitPane;
      import javax.swing.JTextField;
      import javax.swing.SwingUtilities;
      
      public class InterfaceDesign {
      
          private SrcollListener l = new SrcollListener();
      
          public static void main( String args[] ) {
              InterfaceDesign id = new InterfaceDesign();
              id.getPnlProjectDetail( "My Project" );
          }
      
          public void getPnlProjectDetail( String strProjectName ) {
              JPanel pnlPjDetail = new JPanel();
              JPanel pnlPjDetail2 = new JPanel();
              JScrollPane scrPjDetail;
              JScrollPane scrPjDetail2;
              pnlPjDetail.setLayout( null );
              pnlPjDetail.setBounds( 0, 0, 200, 250 );
              pnlPjDetail2.setLayout( null );
              pnlPjDetail2.setBounds( 0, 0, 200, 300 );
              JFrame frmtest = new JFrame();
              JInternalFrame interFrame = new JInternalFrame();
              interFrame.setBounds( 0, 0, 280, 180 );
      
              frmtest.setBounds( 1, 1, 600, 200 );
              pnlPjDetail.setPreferredSize( new Dimension( 200, 250 ) );
              pnlPjDetail2.setPreferredSize( new Dimension( 200, 300 ) );
      
              JLabel lblFirstName = new JLabel( "First Name" );
              JLabel lblLastName = new JLabel( "Last Name" );
              JLabel lblAddress = new JLabel( "Address" );
              JLabel lblCity = new JLabel( "City" );
              JLabel lblZipCode = new JLabel( "Zip Code" );
              JLabel lblPhone = new JLabel( "Phone" );
              JLabel lblEmailID = new JLabel( "Emain Id" );
      
              JTextField tfFirstName = new JTextField();
              JTextField tfLastName = new JTextField();
              JTextField tfAddress = new JTextField();
              JTextField tfCity = new JTextField();
              JTextField tfZipCode = new JTextField();
              JTextField tfPhone = new JTextField();
              JTextField tfEmailID = new JTextField();
      
              lblFirstName.setBounds( 25, 55, 85, 20 );
              tfFirstName.setBounds( 25, 85, 85, 20 );
              tfFirstName.addFocusListener( l );
              pnlPjDetail.add( lblFirstName );
              pnlPjDetail.add( tfFirstName );
      
              lblLastName.setBounds( 25, 115, 85, 20 );
              tfLastName.setBounds( 25, 145, 85, 20 );
              tfLastName.addFocusListener( l );
              pnlPjDetail.add( lblLastName );
              pnlPjDetail.add( tfLastName );
      
              lblAddress.setBounds( 25, 175, 85, 20 );
              tfAddress.setBounds( 25, 205, 85, 20 );
              tfAddress.addFocusListener( l );
              pnlPjDetail.add( lblAddress );
              pnlPjDetail.add( tfAddress );
      
              lblCity.setBounds( 25, 55, 85, 20 );
              tfCity.setBounds( 25, 85, 85, 20 );
              tfCity.addFocusListener( l );
              pnlPjDetail2.add( lblCity );
              pnlPjDetail2.add( tfCity );
      
              lblZipCode.setBounds( 25, 115, 85, 20 );
              tfZipCode.setBounds( 25, 145, 85, 20 );
              tfZipCode.addFocusListener( l );
              pnlPjDetail2.add( lblZipCode );
              pnlPjDetail2.add( tfZipCode );
      
              lblPhone.setBounds( 25, 175, 85, 20 );
              tfPhone.setBounds( 25, 205, 85, 20 );
              tfPhone.addFocusListener( l );
              pnlPjDetail2.add( lblPhone );
              pnlPjDetail2.add( tfPhone );
      
              lblEmailID.setBounds( 25, 235, 85, 20 );
              tfEmailID.setBounds( 25, 265, 85, 20 );
              tfEmailID.addFocusListener( l );
              pnlPjDetail2.add( lblEmailID );
              pnlPjDetail2.add( tfEmailID );
      
              scrPjDetail = new JScrollPane( pnlPjDetail );
              scrPjDetail2 = new JScrollPane( pnlPjDetail2 );
      
              scrPjDetail.setAutoscrolls( true );
      
              //frmtest.setContentPane(scrProjectDetail);
              JSplitPane splitPane = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, scrPjDetail, scrPjDetail2 );
              splitPane.setDividerLocation( 300 );
              interFrame.setContentPane( splitPane );
              interFrame.setVisible( true );
      
              //frmtest.getLayeredPane().add(interFrame);
              frmtest.setContentPane( interFrame );
              //frmtest.add(scrProjectDetail);
              frmtest.setVisible( true );
              frmtest.setResizable( false );
              frmtest.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
      
              //return pnlPjDetail;       
          }
      
          public class SrcollListener implements FocusListener {
      
              @Override
              public void focusGained( FocusEvent e ) {
                  final Component component = e.getComponent();
                  if( component != null && component.getParent() != null && component.getParent() instanceof JComponent ) {
                      SwingUtilities.invokeLater( new Runnable() {
                          @Override
                          public void run() {
                              ((JComponent)component.getParent()).scrollRectToVisible( component.getBounds() );
                          }
                      } );
                  }
              }
      
              @Override
              public void focusLost( FocusEvent e ) {
              }
          }
      }
      

      【讨论】:

      • -1 for null layoutManager(啊..刚刚看到你从 OP 复制了它;-)
      猜你喜欢
      • 2019-09-18
      • 2019-06-20
      • 1970-01-01
      • 2018-06-11
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 2022-11-23
      • 2017-01-05
      相关资源
      最近更新 更多