Swing:如何让JFrame滚动流畅?

tyt*_*ytk 5 java macos swing

我正在创建一个非常简单的 Swing GUI,其中包含一个可滚动文本区域。但是,我在用户界面的响应能力方面遇到了一些问题。这是我用来尝试一些不同配置的类:

public class ComponentSample {
  public void createWindow() {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    TextArea textArea = new TextArea(getFillerText(), 40, 120);

    setViews(frame, textArea);

    frame.pack();
    frame.setVisible(true);
  }

  private void setViews(JFrame frame, TextArea textArea) {
    ...
  }

  private String getFillerText() {
    String threeLines = "Why isn't scrolling smooth? \nSteve Jobs would not approve\n\n";
    StringBuilder stringBuilder = new StringBuilder();
    for (int i = 0; i < 100; i++) {
      stringBuilder.append(threeLines);
    }
    return stringBuilder.toString();
  }

  public static void main(String[] args) throws Exception {
    SwingUtilities.invokeLater(
      () -> new ComponentSample().createWindow()
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

这里我对该方法尝试了几种不同的方法体,setViews()结果如下:

在 JPanel 中添加文本区域

    JPanel panel = new JPanel();
    panel.add(textArea);
    frame.add(panel);
Run Code Online (Sandbox Code Playgroud)

按 1 或 2 行滚动而不是按像素滚动(不平滑)。快速滚动时也会闪烁白色(重新绘制问题?)。

在视口中添加文本区域

    JViewport viewport = new JViewport();
    viewport.setView(textArea);
    frame.add(viewport);
Run Code Online (Sandbox Code Playgroud)

与 JPanel 的结果相同。

直接添加textArea

    frame.add(textArea);
Run Code Online (Sandbox Code Playgroud)

与 JPanel 的结果相同。

在 ScrollPane 中添加 textArea

    JScrollPane scrollPane = new JScrollPane(textArea);
    scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
    frame.add(scrollPane);
Run Code Online (Sandbox Code Playgroud)

似乎无法正确处理滚动 - 它不响应滚轮(仅单击滚动条)。按约 0.5 行而不是按像素滚动。

另外,在上述每种情况下,当用鼠标拖动滚动条时,它会在标准输出中打印以下内容(错误?IDEA 将其显示为红色):

2016-03-17 09:45:33.413 java[40977:2845515] inOptions: {
    JavaCUIThumbStartKey = 0;
    "is.flipped" = 0;
    kCUIOrientationKey = kCUIOrientVertical;
    kCUIThumbProportionKey = "0.1328903585672379";
    max = 0;
    pressedpart = 0;
    state = normal;
    value = 0;
    widget = scrollbar;
}
2016-03-17 09:45:33.413 java[40977:2845515] inOptions: {
    JavaCUIThumbStartKey = 0;
    "is.flipped" = 0;
    kCUIOrientationKey = kCUIOrientVertical;
    kCUIThumbProportionKey = "0.1328903585672379";
    max = 0;
    pressedpart = 0;
    state = normal;
    value = 0;
    widget = scrollbar;
}
2016-03-17 09:45:33.414 java[40977:2845515] outHitPart = 0
Run Code Online (Sandbox Code Playgroud)

如果重要的话,我使用的是 OS X。

我的问题是如何使其平滑滚动(逐像素)而不闪烁白色且没有打印错误?

tyt*_*ytk 3

正如 camickr 指出的那样,我应该使用 aJTextArea而不是TextArea. 这解决了我认为是重画问题的白色闪光。

为了使滚动平滑而不滞后,我必须调整单位增量,即按下滚动条箭头之一时视图将移动的像素数:

scrollPane.getVerticalScrollBar().setUnitIncrement(1);
Run Code Online (Sandbox Code Playgroud)

这最终导致了一堆延迟,因为当我滚动时,程序必须以 1px 的间隔连续计算和重绘屏幕。为了解决这个问题,我必须更改视口滚动模式:

scrollPane.getViewport().setScrollMode(JViewport.BACKINGSTORE_SCROLL_MODE);
Run Code Online (Sandbox Code Playgroud)

原来是BLIT_SCROLL_MODE默认使用的。

我现在拥有的应用程序可以平滑滚动(不会跳跃像素),不会滞后,并且当我滚动太快时不会闪烁白色。这已经是相当不错的进步了。我还没有弄清楚的一件事是,现在滚动速度相当慢 - 例如,使鼠标滚轮旋转良好并不会让我在页面上走得太远。这显然是因为我每个凹口只移动一个像素(这就是所做setUnitIncrement()的)。我想要的是当我积极滚动时,在页面上向下移动一段距离,就像在 Chrome 或 Intellij 等其他应用程序中一样。但目前我对自己所拥有的一切感到非常满意。