简体   繁体   English

使用Nimbus外观在JTable中水平滚动

[英]Scroll horizontally in JTable with Nimbus look and feel

I have a JTable that is wider than the JScrollPane it is contained in (essentially defined like this): 我有一个比它包含的JScrollPane宽的JTable(基本上定义如下):

JTable table = new JTable(model);
// I change some things like disallowing reordering, resizing,
// disable column selection, etc.

// I set the default renderer to a DefaultTableCellRenderer
// getTableCellRendererComponent, and then changes the color
// of the cell text depending on the cell value

JPanel panel = new JPanel(new BorderLayout(0, 5));
panel.add(new JScrollPane(table), BorderLayout.CENTER);
// add other stuff to the panel
this.add(panel,  BorderLayout.CENTER);

Before I changed the look and feel from the default to Nimbus, I was able to scroll left and right in the JTable. 在我将默认的外观改为Nimbus之前,我能够在JTable中左右滚动。 (I like the Mac LaF, but it isn't supported on Windows, and the Windows LaF is ugly in my opinion), (我喜欢Mac LaF,但它在Windows上不受支持,而且我认为Windows LaF很丑陋),

I took the following code straight from the Java Tutorials: 我直接从Java教程中获取了以下代码:

try {
    for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
        if ("Nimbus".equals(info.getName())) {
            UIManager.setLookAndFeel(info.getClassName());
            break;
        }
    }
} catch (Exception e) {
    // If Nimbus is not available, you can set the GUI to another look
    // and feel.
}

I recompiled and ran the code without changing any of the table definition stuff above, and I couldn't scroll horizontally in the JTable anymore. 我重新编译并运行代码而不更改上面的任何表定义,我无法再在JTable中水平滚动。

I can't seem to find anything on what would cause this. 我似乎无法找到导致这种情况的任何内容。 Is this the normal behavior for Nimbus, or can I change it? 这是Nimbus的正常行为,还是可以更改它? If so, how? 如果是这样,怎么样? or should I just try a different look and feel? 或者我应该尝试不同的外观和感觉?

EDIT: 编辑:

I discovered two things: 我发现了两件事:

  1. I made a new class extending JTable to test this. 我创建了一个扩展JTable的新类来测试它。 I copied the code for getScrollableUnitIncrement from the JTable source, and added print statements. 我从JTable源复制了getScrollableUnitIncrement的代码,并添加了print语句。 The orientation that is passed seems to always be SwingConstants.VERTICAL , while in the default Look and Feel (Mac Aqua or whatever), both horizontal and vertical scrolling works. 传递的方向似乎总是SwingConstants.VERTICAL ,而在默认的外观(Mac Aqua或其他)中,水平和垂直滚动都有效。 I don't know why this is. 我不知道为什么会这样。

  2. Another part of the project also relies on horizontal scrolling. 该项目的另一部分还依赖于水平滚动。 I tested it with both LaFs, and it worked fine in the default, but Nimbus would not allow me to scroll horizontally, either. 我用两个LaF测试了它,它在默认情况下工作正常,但是Nimbus也不允许我水平滚动。

Could this be a bug with Nimbus? 这可能是Nimbus的错误吗?

Either way, I guess I'm going to use a different Look and Feel... 无论哪种方式,我想我将使用不同的外观和感觉......

EDIT #2: 编辑#2:

I should have mentioned this before. 我之前应该提到这一点。 I am able to scroll horizontally with the scroll bar in the window, but not with my track pad or scroll wheel on my mouse. 能够在窗口滚动条水平滚动,但不符合我的跟踪垫或在我的鼠标滚轮。

(Note: After writing this, I found a solution, which appears in the addendum of this post.) (注意:写完之后,我找到了一个解决方案,该解决方案出现在本文的附录中。)

To reproduce the problem, you need to make the scroll bars required. 要重现该问题,您需要使滚动条成为必需。 (This is why some people have trouble reproducing this bug.) This means the obvious workaround is to make your horizontal scroll bar optional. (这就是为什么有些人在重现这个bug时遇到麻烦。)这意味着明显的解决方法是让你的水平滚动条可选。 (This is not always practical.) (这并不总是实用的。)

You will only see the bug when you drag the window's width out to more than 1200 pixels or so. 将窗口宽度拖动到1200像素左右时,您只会看到错误。 Until then, the scroll bar will work fine. 在此之前,滚动条将正常工作。

And the problem only shows up in Nimbus. 而这个问题只出现在Nimbus上。 (It may show up in other L&Fs created from the SynthLookAndFeel, but I haven't investigated that yet.) (它可能出现在从SynthLookAndFeel创建的其他L&F中,但我还没有调查过。)

I've found that the spurious scroll bar thumb only shows up when you have no need to scroll, so it's just a visual bug. 我发现当你不需要滚动时,虚假的滚动条拇指只显示出来,所以这只是一个视觉错误。 When you need to scroll, the scroll bar thumb will appear and will work properly, although it might not be the right size. 当您需要滚动时,滚动条拇指将出现并且将正常工作,尽管它可能不是正确的尺寸。 This may be why it hasn't been fixed yet. 这可能是它尚未修复的原因。

Here's an example where you can compare the different L&Fs. 这是一个可以比较不同L&F的例子。 In this example, Choose Nimbus, then drag the width inward and watch how the size of the scroll bar changes. 在此示例中,选择Nimbus,然后向内拖动宽度并观察滚动条的大小如何变化。 When you're wider than the background image, the spurious scroll bar will be visible. 当您比背景图像宽时,将显示虚假滚动条。 As soon as you get narrower, a valid scroll bar thumb will appear, but it will be a bit too small. 一旦变窄,就会出现一个有效的滚动条拇指,但它会有点太小。 As you get smaller, the scroll bar thumb will stay a constant size until you reach a certain point, (at viewport width of 1282 pixels) then it will start getting smaller like it's supposed to. 当你变小时,滚动条拇指将保持恒定的大小,直到你到达某个点,(在视口宽度为1282像素)然后它将开始变小,就像它应该的那样。

With any other L&F, as soon as you get narrower than the background image, a thumb will appear that almost fills its space. 对于任何其他L&F,只要你比背景图像更窄,就会出现一个几乎填满其空间的拇指。 It gets smaller as the window gets smaller, like it's supposed to. 随着窗口变小,它变得越来越小,就像它应该的那样。

(This exercise will also reveal how Nimbus draws much more slowly than any other L&F.) (这项练习还将揭示Nimbus如何比任何其他L&F吸引得慢得多。)

You can observe different, but still incorrect behavior by making the icon smaller. 您可以通过缩小图标来观察不同但仍然不正确的行为。 Try 800 x 450. The spurious scroll bar will appear when the viewport width is > 1035. (Viewport size is shown at the bottom of the window.) 尝试800 x 450.当视口宽度> 1035时,将出现虚假滚动条。(视口大小显示在窗口底部。)

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
/**
 * NimbusScrollBug
 * <p/>
 * @author Miguel Muñoz
 */
public class NimbusScrollBug extends JPanel {
  private static final long serialVersionUID = -4235866781219951631L;
  private static JFrame frame;
  private static boolean firstTime = true;
  private static Point location;
  private static final UIManager.LookAndFeelInfo[] INFOS
          = UIManager.getInstalledLookAndFeels();

  private final JLabel viewPortLabel = new JLabel();

  public static void main(final String[] args) {
    makeMainFrame(new NimbusScrollBug(), "System");
  }

  public static void makeMainFrame(final NimbusScrollBug mainPanel,
                                   final String name) {
    if (firstTime) {
      installLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    }
    frame = new JFrame(name);
    final Container contentPane = frame.getContentPane();
    contentPane.setLayout(new BorderLayout());
    contentPane.add(mainPanel, BorderLayout.CENTER);
    contentPane.add(makeButtonPane(mainPanel), BorderLayout.LINE_START);
    frame.setLocationByPlatform(firstTime);
    frame.pack();
    frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    frame.setVisible(true);
    if (firstTime) {
      location = frame.getLocation();
    } else {
      frame.setLocation(location);
    }
    frame.addComponentListener(new ComponentAdapter() {
      @Override
      public void componentMoved(final ComponentEvent e) {
        location = e.getComponent().getLocation();
      }
    });
    firstTime = false;
  }

  private static JPanel makeButtonPane(final NimbusScrollBug mainPanel) {
    JPanel innerButtonPanel = new JPanel(new GridBagLayout());
    GridBagConstraints constraints = new GridBagConstraints();
    constraints.fill = GridBagConstraints.BOTH;
    constraints.gridx = 0; // forces vertical layout.
    for (final UIManager.LookAndFeelInfo lAndF : INFOS) {
      final JButton button = new JButton(lAndF.getName());
      button.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(final ActionEvent e) {
          frame.dispose();
          installLookAndFeel(lAndF.getClassName());
          makeMainFrame(new NimbusScrollBug(), lAndF.getName());
        }
      });
      innerButtonPanel.add(button, constraints);
    }
    final String version = System.getProperty("java.version");
    JLabel versionLabel = new JLabel("Java Version " + version);
    innerButtonPanel.add(versionLabel, constraints);

    JPanel outerButtonPanel = new JPanel(new BorderLayout());
    outerButtonPanel.add(innerButtonPanel, BorderLayout.PAGE_START);
    return outerButtonPanel;
  }

  private static void installLookAndFeel(final String className) {
    //noinspection OverlyBroadCatchBlock
    try {
      UIManager.setLookAndFeel(className);
    } catch (Exception e) {
      //noinspection ProhibitedExceptionThrown
      throw new RuntimeException(e);
    }
  }

  private NimbusScrollBug() {
    Icon icon = new Icon() {
      @Override
      public void paintIcon(final Component c, final Graphics g, 
                            final int x, final int y) {
        Graphics2D g2 = (Graphics2D) g;
        g2.translate(x, y);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                RenderingHints.VALUE_ANTIALIAS_ON);
        Stroke lineStroke = new BasicStroke(6.0f);
        g2.setStroke(lineStroke);
        g2.setColor(Color.white);
        g2.fillRect(0, 0, getIconWidth(), getIconHeight());
        g2.setColor(Color.RED);
        g2.drawLine(0, 0, getIconWidth(), getIconHeight());
        g2.drawLine(0, getIconHeight(), getIconWidth(), 0);
        g2.dispose();
      }

      @Override
      public int getIconWidth() {
        return 1600;
      }

      @Override
      public int getIconHeight() {
        return 900;
      }
    };
    JLabel label = new JLabel(icon);
    setLayout(new BorderLayout());
    final JScrollPane scrollPane = new JScrollPane(label,
            ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
            ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
    label.addHierarchyBoundsListener(new HierarchyBoundsAdapter() {
      @Override
      public void ancestorResized(final HierarchyEvent e) {
        viewPortLabel.setText("ViewPort Size: " 
                + scrollPane.getViewport().getSize());
      }
    });
    add(scrollPane, BorderLayout.CENTER);
    add(viewPortLabel, BorderLayout.PAGE_END);
  }
}

Addendum: Further investigation revealed the problem. 附录:进一步调查显示了这个问题。 The NimbusDefaults class, which creates the UIDefaults instance for Nimbus, has this line: 为Nimbus创建UIDefaults实例的NimbusDefaults类具有以下特性:

d.put("ScrollBar.maximumThumbSize", new DimensionUIResource(1000, 1000));

Any other look and feel uses 4096 for both values (so, for really big monitors, they will show the same behavior). 任何其他外观都使用4096两个值(因此,对于非常大的显示器,它们将显示相同的行为)。

The following method, which may be used to install any look and feel, will fix this problem: 以下方法可用于安装任何外观,将解决此问题:

private static void installLookAndFeel(final String className) {
  //noinspection OverlyBroadCatchBlock
  try {
    Class<?> lnfClass = Class.forName(className, true,
            Thread.currentThread().getContextClassLoader());
    final LookAndFeel lAndF;
    lAndF = (LookAndFeel) lnfClass.getConstructor().newInstance();

    // Reset the defaults after instantiating, but before
    // calling UIManager.setLookAndFeel(). This fixes the Nimbus bug
    DimensionUIResource dim = new DimensionUIResource(4096, 4096);
    lAndF.getDefaults().put("ScrollBar.maximumThumbSize", dim);
    UIManager.setLookAndFeel(lAndF);
  } catch (Exception e) {
    final String systemName = UIManager.getSystemLookAndFeelClassName();
    // Prevents an infinite recursion that's not very likely...
    // (I like to code defensively)
    if (!className.equals(systemName)) {
      installLookAndFeel(systemName);
    } else {
      // Feel free to handle this any other way.
      //noinspection ProhibitedExceptionThrown
      throw new RuntimeException(e);
    }
  }
}

Of course, you can fix the problem for really big monitors by using a bigger value. 当然,您可以通过使用更大的值来解决真正大型显示器的问题。

I confirmed that the vertical scroll bar has exactly the same problem, but is only seen when the window gets very large vertically. 我确认垂直滚动条有完全相同的问题,但仅在窗口垂直变大时才能看到。 This is why this problem is usually only seen with the horizontal scroll bar. 这就是为什么这个问题通常只能在水平滚动条中看到的原因。

Based on the information you provided, I'm not able to recreate your problem (and therefore not able to help you figure out what's going wrong). 根据您提供的信息,我无法重新创建您的问题(因此无法帮助您弄清楚出了什么问题)。 Here's a sscce that works for me. 这是一个适合我的sscce。 Can you reproduce the problem with this example? 你能用这个例子重现这个问题吗? Perhaps the problem is trickling down from a different part of the application. 也许问题是从应用程序的不同部分逐渐减少。

public static void main(String[] args){
    try {
        for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
                UIManager.setLookAndFeel(info.getClassName());
                break;
            }
        }
    } catch (Exception e) {
        // If Nimbus is not available, you can set the GUI to another look and feel.
    }

    //Create Frame
    JFrame frame = new JFrame("Title");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    //Create Table
    JTable table = new JTable(0, 2);
    ((DefaultTableModel) table.getModel()).addRow(new Object[]{"Sample Text", "Hi Mom!"});
    table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);

    // Wrap table in Scroll pane and add to frame
    frame.add(new JScrollPane(table), BorderLayout.CENTER);

    // Finish setting up the frame and display
    frame.setBounds(0, 0, 600,400);
    frame.setPreferredSize(new Dimension(600, 400));
    frame.pack();       
    frame.setVisible(true);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM