Java Swing:如何使JFrame滚动平滑?

Java Swing:如何使JFrame滚动平滑?,java,macos,swing,Java,Macos,Swing,我正在创建一个非常简单的Swing GUI,其中包含一个可滚动的文本区域。但是,我在UI的响应性方面遇到了一些问题。下面是我用来尝试一些不同配置的类: public class ComponentSample { public void createWindow() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); TextArea

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

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()
    );
  }
}
按1或2行滚动,而不是按像素滚动(不平滑)。快速滚动时也会闪烁白色(重新绘制问题?)

在视口中添加文本区域

    JViewport viewport = new JViewport();
    viewport.setView(textArea);
    frame.add(viewport);
与JPanel的结果相同

直接添加文本区域

    frame.add(textArea);
与JPanel的结果相同

在滚动窗格中添加文本区域

    JScrollPane scrollPane = new JScrollPane(textArea);
    scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
    frame.add(scrollPane);
似乎无法正确处理滚动-它不会响应滚轮(仅单击滚动条)。按~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
如果重要的话,我在OSX上


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

不要使用AWT组件的文本区域

相反,您应该使用一个
JTextArea
,它是Swing组件

基本准则是:

JTextArea textArea = new JTextArea(5, 20);
JScrollPane scrollPane = new JScrollPane( textArea );
frame.add( scrollPane );
编辑:

它仍然按整行滚动,而不是按像素滚动

滚动窗格支持单元滚动和块滚动的概念。JTextArea的单位滚动是一行文本,块滚动是视口窗口的大小。使用鼠标时,滚动(在windows中)基于单位滚动的倍数,在我的组件上是单位滚动的3倍

如果您关心的是鼠标滚轮的滚动量,那么您可以使用,它允许您将乘数更改为不同的值。所以你可以把它设为1

如果您关心的是实际单位增量金额,则可以使用以下方法更改默认值:

scrollPane.getVerticalScrollBar().setUnitIncrement(1);

正如卡米克尔指出的,我应该使用
JTextArea
而不是
TextArea
。这修复了我认为是重绘问题的白色闪光

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

scrollPane.getVerticalScrollBar().setUnitIncrement(1);
这最终导致了一系列的延迟,因为当我滚动时,程序必须以1px的间隔连续计算和重新绘制屏幕。要解决此问题,我必须更改视口滚动模式:

scrollPane.getViewport().setScrollMode(JViewport.BACKINGSTORE_SCROLL_MODE);
原来它默认使用的是
BLIT\u SCROLL\u模式


我现在拥有的是一个应用程序,它可以平滑地滚动(不会跳跃像素),不会延迟,当我滚动过快时也不会闪烁白色。这是相当好的进步。有一件事我还没有弄明白,现在滚动速度相当慢——例如,让鼠标滚轮好好旋转并不会让我在页面上走得太远。这显然是因为我每一个凹口只移动一个像素(这就是
setUnitIncrement()
所做的)。我想要的是,当我快速滚动页面时,能在页面上向下移动一段距离,就像Chrome或Intellij等其他应用程序一样。但现在我对自己的成就非常满意。

谢谢!这解决了重绘问题。但是,它仍然按整行滚动,而不是按像素滚动,这使得动画非常粗糙。如何才能顺利滚动?此外,拖动滚动条时,(错误?)消息仍会打印到标准输出。不确定这是否是预期的。谢谢。我按照您的建议使用了
setUnitIncrement()
。这会使滚动更加平滑,但不幸的是,滚动会滞后。通过阅读文档,我收集到每个像素卷轴上都在重新计算整个视图。我添加了一个
JViewport
,根据文档,它只更新必要的区域,还预加载整个组件。我现在在
JViewport
内有一个
JTextArea
,在
JScrollPane
内有一个
JFrame
。但是,这可能会导致滚动条消失,因为视口本身在滚动面板的大小范围内。如何获取文本区域的滚动条?@tytk,我提供了在JScrollPane中使用JTextArea所需的3行代码。您永远不必玩视口。视口由滚动窗格管理。我从未体验过拉格滚动,但我使用Windows。我建议你发布一个合适的帖子,这样使用OSX的人就可以测试你的代码,看看他们是否遇到同样的问题。否则我不知道该说什么。使用滚动窗格没有什么神奇之处。
scrollPane.getViewport().setScrollMode(JViewport.BACKINGSTORE_SCROLL_MODE);