5

含まれている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);

ルックアンドフィールをデフォルトからニンバスに変更する前は、JTableを左右にスクロールすることができました。(私はMac LaFが好きですが、Windowsではサポートされておらず、Windows LaFは私の意見では醜いです)、

次のコードを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.
}

上記のテーブル定義を変更せずにコードを再コンパイルして実行したところ、JTableを水平方向にスクロールできなくなりました。

これを引き起こす原因については何も見つからないようです。これはNimbusの通常の動作ですか、それとも変更できますか?もしそうなら、どのように?または、別のルックアンドフィールを試してみるべきですか?

編集:

私は2つのことを発見しました:

  1. これをテストするために、JTableを拡張する新しいクラスを作成しました。getScrollableUnitIncrementJTableソースからのコードをコピーし、 printステートメントを追加しました。渡される方向は常にであるように見えSwingConstants.VERTICALますが、デフォルトのルックアンドフィール(Mac Aquaなど)では、水平スクロールと垂直スクロールの両方が機能します。なぜなのかわかりません。

  2. プロジェクトの別の部分も、水平スクロールに依存しています。両方のLaFでテストしたところ、デフォルトでは正常に機能しましたが、Nimbusでは水平方向にスクロールすることもできませんでした。

これはニンバスのバグでしょうか?

いずれにせよ、私は別のルックアンドフィールを使用するつもりだと思います...

編集#2:

私は前にこれについて言及すべきでした。ウィンドウのスクロールバーを使用して水平方向にスクロールすることできますが、マウスのトラックパッドまたはスクロールホイールを使用することはできません。

4

2 に答える 2

3

(注:これを書いた後、この投稿の補遺に記載されている解決策を見つけました。)

問題を再現するには、スクロール バーを必須にする必要があります。(これが、このバグの再現に苦労する人がいる理由です。) これは、水平スクロール バーをオプションにすることが明らかな回避策であることを意味します。(これは必ずしも実用的ではありません。)

ウィンドウの幅を 1200 ピクセル以上にドラッグした場合にのみバグが表示されます。それまでは、スクロール バーは正常に機能します。

そして、問題は Nimbus でのみ発生します。(SynthLookAndFeel から作成された他の L&F に表示される可能性がありますが、まだ調査していません。)

偽のスクロール バー サムは、スクロールする必要がない場合にのみ表示されることがわかりました。これは単なる視覚的なバグです。スクロールする必要がある場合、スクロール バーのつまみが表示され、適切に機能しますが、サイズが適切でない場合があります。これが、まだ修正されていない理由かもしれません。

さまざまな L&F を比較できる例を次に示します。この例では、Nimbus を選択し、幅を内側にドラッグして、スクロール バーのサイズがどのように変化するかを確認します。背景画像より幅が広い場合、偽のスクロール バーが表示されます。狭くなるとすぐに、有効なスクロール バーのつまみが表示されますが、少し小さすぎます。縮小すると、スクロール バーのつまみは、特定のポイント (ビューポート幅 1282 ピクセル) に到達するまで一定のサイズのままになります。その後、想定どおりに縮小し始めます。

他の L&F では、背景画像よりも狭くなるとすぐに、そのスペースをほぼ埋める親指が表示されます。ウィンドウが小さくなると、想定どおりに小さくなります。

(この演習では、Nimbus が他のどの L&F よりも描画が遅いことも明らかにします。)

アイコンを小さくすることで、異なる動作を確認できますが、それでも正しくありません。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);
  }
}

補遺:さらなる調査により、問題が明らかになりました。Nimbus の UIDefaults インスタンスを作成する NimbusDefaults クラスには、次の行があります。

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

他のすべてのルック アンド フィールでは、両方の値に 4096 が使用されます (したがって、非常に大きなモニターでは、同じ動作が表示されます)。

任意のルック アンド フィールをインストールするために使用できる次の方法で、この問題を解決できます。

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);
    }
  }
}

もちろん、より大きな値を使用することで、非常に大きなモニターの問題を解決できます。

垂直スクロール バーにもまったく同じ問題があることを確認しましたが、ウィンドウが垂直方向に非常に大きくなった場合にのみ表示されます。これが、この問題が通常、水平スクロール バーでのみ見られる理由です。

于 2015-02-04T02:56:26.623 に答える
1

あなたが提供した情報に基づいて、私はあなたの問題を再現することができません(したがって、あなたが何が悪いのかを理解するのを助けることができません)。これが私のために働くsscceです。この例で問題を再現できますか?おそらく、問題はアプリケーションの別の部分から流れ落ちていることです。

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);
}
于 2012-07-16T18:44:17.543 に答える