【问题标题】:How to effectively add a call back method for the JPanel.add(component) method?如何有效地为 JPanel.add(component) 方法添加回调方法?
【发布时间】:2012-01-06 21:02:57
【问题描述】:

编辑:现已解决,但两天内无法标记为已接受

在我的课堂上,我有一个JScrollPanel,里面也有一个JPanel

我的代码类似于这样:

import java.awt.Color;
import java.awt.Container;
import javax.swing.*;

public class MyClass {

    private JPanel p;
    private JScrollPane s;
    private Container contentPane;

    public MyClass(Container contentPane) {
        this.contentPane = contentPane;
        this.p = new JPanel();
        this.p.setBackground(Color.WHITE);
        BoxLayout boxLayout = new BoxLayout(this.p, BoxLayout.Y_AXIS);
        this.p.setLayout(boxLayout);
        this.s = new JScrollPane(this.p);
        this.s.setSize(400, 364);
        this.contentPane.add(this.s);
    }

    public final JLabel makeJLabel(String message) {
        JLabel jLabel = new JLabel("<html><p style=\"padding-left:15px;padding-right:15px;width:280px;\">" + message.replaceAll("(\r\n|\n)", "<br />") + "</p></html>");
        /*
        some stuff here to calculate pref/max size and add an imageicon
        */
        p.add(jLabel);
        this.p.revalidate();
        this.s.revalidate(); //just added because the above line made no effect
        scrollToBottom();
        return jLabel;
    }

    public void scrollToBottom() {
        JScrollBar vertical = s.getVerticalScrollBar();
        vertical.setValue(vertical.getMaximum());
    }
}

在我班的其他地方,我有一个方法可以将JLabel 添加到JPanel。这个实际方法很长,所以我不会全部发布,但这是将其添加到面板的代码:p.add(jLabel1);

感谢 Box Layout,所有这些 JLabels 都以垂直方式添加。

JLabel 添加到 JPanel 之后,我希望 JScrollPane 滚动到底部。但这要等到JPanel 实际被绘制(绘制?)到JPanel 上之后才能完成。否则我会得到这个结果:

所以我想要做的是向JPanel 添加某种形式的监听器,它检测我的JLabel 何时被绘制到它,这样我就可以告诉我的JScrollPane 滚动到底部。我已经编写了一个将窗格滚动到底部的方法,但是我还没有适合调用它的地方。

请问有人对此有什么想法吗?谢谢。

【问题讨论】:

    标签: java swing


    【解决方案1】:

    我假设您只是希望标签在滚动窗格中可见,所以我猜您应该能够执行以下操作:

    panel.add( label );
    panel.revalidate();
    label.scrollRectToVisible( label.getBounds() );
    

    或者,如果您真的只想滚动底部,那么您可以执行以下操作:

    panel.revalidate();
    scrollPane.getVerticalScrollBar().setValue( getVerticalScrollBar().getMaximum() );
    

    这两个答案都假设 GUI 已经可见。

    【讨论】:

    • 您好,谢谢您的回答。我正在/正在使用第二种解决方案,但问题是在调用时尚未添加正在添加的组件(已添加,但尚未绘制)。所以滚动条滚动到底部,但随后添加了一些额外的东西(绘制)。我现在已经在我的帖子中解决了它,但我不能接受我自己的答案 2 天。
    • 我阅读了您的解决方案。听起来太复杂了。组件的绘画不相关。重要的是所有组件都已经过验证并具有首选大小,这就是 revalidate() 很重要的原因。如果我的建议不起作用,请发布您的 SSCCE 来证明问题,因为我确信有一种简单的方法可以解决问题。
    • JLabel 手动设置了首选尺寸和最大尺寸,但我仍然无法让此解决方案正常工作。我不知道如何将组件添加到 JPanel 的来龙去脉,但它看起来好像正在滚动到可见内容的底部,并且在滚动后添加了最终组件。我现在将发布 SSCCE。
    • @kleopatra,不知道你为什么不喜欢第二个。这些建议针对两种不同的要求。第一个确保组件可见,第二个确保滚动窗格位于底部。
    • 几个原因 a) 违反了始终使用可用的最高抽象的一般规则 b) 保留对 scrollPane 的引用导致代码污染 c) 深入到引用的类是一种不好的气味,无论是 api或它的用法。不,我不认为这些是不同的用例:要显示的“底部”是 panel 的底部,而不是滚动窗格的底部(实际上,它可能是完全不同的类型最终满足需求的父级)要在最高抽象级别上实现这一点,就是滚动到面板底部的一个矩形
    【解决方案2】:

    Rob 回答的第一部分是要走的路 - 缺少的部分是将 scrollRectToVisible 包装到 SwingUtilities.invokeLater 中。这样做会延迟滚动,直到所有待处理的事件都被处理,也就是说,直到所有内部状态都被更新。一个代码sn-p(在swingx测试支持说话,只需用手动创建的代码替换框架创建和滚动面板包装)

    final JComponent panel = new JPanel();
    panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
    for(int i = 0; i < 10; i++)
        panel.add(new JLabel("initial message " + i));
    JXFrame frame = wrapWithScrollingInFrame(panel, "scroll to bottom");
    Action action = new AbstractAction("addMessage") {
        int count;
        @Override
        public void actionPerformed(ActionEvent e) {
            final JLabel label = new JLabel("added message " + count++);
            panel.add(label);
            panel.revalidate();
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    label.scrollRectToVisible(label.getBounds());
                }
            });
        }
    };
    frame.add(new JButton(action), BorderLayout.SOUTH);
    show(frame);
    

    【讨论】:

      【解决方案3】:

      为此困扰了我好久,但在问了这个问题后,我终于想通了。

      要解决我的问题,我需要做的就是监听 JScrollPane 滚动条中值的变化。如果它发生了变化,请进行一些计算,并在必要时滚动到底部。

      必须注意确保您不会覆盖移动滚动条的用户。 特别是您正在查看track,它是AdjustmentEvent。当用户移动滚动条时也会触发此事件。

      为了让用户在不强制滚动到底部的情况下滚动,我总是跟踪最大滚动条值。如果在触发 track 时新的最大值高于当前值,则添加了一个新项目,我们应该考虑滚动到底部。如果值相等,则用户正在滚动滚动条,我们什么也不做。

      事件监听器可以在这个网站上找到并且可以很容易地工作:Listening for Scrollbar Value Changes in a JScrollPane Container

      import java.awt.Color;
      import java.awt.Container;
      import java.awt.event.AdjustmentEvent;
      import java.awt.event.AdjustmentListener;
      import javax.swing.*;
      
      public class MyClass {
      
          private JPanel p;
          private JScrollPane s;
          private Container contentPane;
          private int scrollBarMax;    
      
          public MyClass(Container contentPane) {
      
              this.contentPane = contentPane;
              this.p = new JPanel();
              this.p.setBackground(Color.WHITE);
              BoxLayout boxLayout = new BoxLayout(this.p, BoxLayout.Y_AXIS);
              this.p.setLayout(boxLayout);
              this.s = new JScrollPane(this.p);
              this.s.setSize(400, 364);
              this.contentPane.add(this.s);
      
              this.s.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener(){
                  @Override
                  public void adjustmentValueChanged(AdjustmentEvent evt) {
                      if (evt.getValueIsAdjusting()) {
                          return;
                      }
                      if (evt.getAdjustmentType() == AdjustmentEvent.TRACK) {
                          if (scrollBarMax < s.getVerticalScrollBar().getMaximum()) {
                              if ((s.getVerticalScrollBar().getValue() + s.getVerticalScrollBar().getSize().height) == scrollBarMax) {
                                  //scroll bar is at the bottom, show the last added JLabel
                                  scrollBarMax = s.getVerticalScrollBar().getMaximum();
                                  scrollToBottom();
                              } else {
                                  //scroll bar is not at the bottom, user has moved it
                                  scrollBarMax = s.getVerticalScrollBar().getMaximum();
                              }
                          }
                      }
                  }
              });
      
              scrollBarMax = s.getVerticalScrollBar().getMaximum();
      
          }
      
          public final JLabel makeJLabel(String message) {
      
              JLabel jLabel = new JLabel("<html><p style=\"padding-left:15px;padding-right:15px;width:280px;\">" + message.replaceAll("(\r\n|\n)", "<br />") + "</p></html>");
      
              /*
              some stuff here to calculate pref/max size and add an imageicon
              */
              p.add(jLabel);
              scrollBarMax = s.getVerticalScrollBar().getMaximum();
              return jLabel;
          }
      
          public void scrollToBottom() {
              JScrollBar vertical = s.getVerticalScrollBar();
              vertical.setValue(vertical.getMaximum());
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-11-30
        • 2012-12-30
        • 1970-01-01
        • 2012-01-22
        • 2012-07-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多