更改自定义组件的 JTable 的 LookAndFeel

Changing LookAndFeel of JTable of Custom Component

我正在更改由 Class 扩展的 JPanel 填充的 JTable 的 LookAndFeel,但我做不到。

我编辑了我的代码,但它仍然很长。

public class LAF_TableCustomContainer extends JFrame {

    public LAF_TableCustomContainer()  {
      setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      setSize(300, 300);
      setVisible(true);
      setLocationRelativeTo(null);
    }

  public static void changeLAF(Container container, String laf) {
    try {
      UIManager.setLookAndFeel(laf);
    } catch (ClassNotFoundException | InstantiationException
        | IllegalAccessException | UnsupportedLookAndFeelException e) {
    }
    SwingUtilities.updateComponentTreeUI(container);
  }

  static final JFrame frame = new JFrame();

  public JComponent makeUI() {

    String[] hdrsObjects = {"PanelSpinnerRadioButton Class Column"};
    Object[][] objectMatrix = new Object[3][1];
    objectMatrix[0][0] = new PanelSpinnerRadioButtonData(false, 10, 40);
    objectMatrix[1][0] = new PanelSpinnerRadioButtonData(true,  20, 40);
    objectMatrix[2][0] = new PanelSpinnerRadioButtonData(false, 30, 40);

    JTable table = new JTable(new DefaultTableModel(objectMatrix, hdrsObjects));
    JScrollPane scrollPane = new JScrollPane(table);
    scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
    scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
    table.setRowHeight(30);

    TableColumn tc = table.getColumn("PanelSpinnerRadioButton Class Column");
    tc.setCellRenderer(new PSRBTableCellRenderer());
    tc.setCellEditor(new PSRBTableCellEditor());

    JPanel pH = new JPanel();
    pH.setLayout(new BoxLayout(pH, BoxLayout.LINE_AXIS));

    JButton bMetal = new JButton("Metal");
    bMetal.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent evt) {
          changeLAF(LAF_TableCustomContainer.this, "javax.swing.plaf.metal.MetalLookAndFeel");
          changeLAF(table, "javax.swing.plaf.metal.MetalLookAndFeel");
          changeLAF(scrollPane, "javax.swing.plaf.metal.MetalLookAndFeel");
          changeLAF((JPanel)table.getModel().getValueAt(0, 0), "javax.swing.plaf.metal.MetalLookAndFeel");
      }
    });

    JButton bMotif = new JButton("Motif");
    bMotif.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent evt) {
          changeLAF(LAF_TableCustomContainer.this, "com.sun.java.swing.plaf.motif.MotifLookAndFeel");
          changeLAF(table, "com.sun.java.swing.plaf.motif.MotifLookAndFeel");
          changeLAF(scrollPane, "com.sun.java.swing.plaf.motif.MotifLookAndFeel");
          changeLAF((JPanel)table.getModel().getValueAt(0, 0), "com.sun.java.swing.plaf.motif.MotifLookAndFeel");
      }
    });

    JButton bNimbus = new JButton("Nimbus");
    bNimbus.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent evt) {
          changeLAF(LAF_TableCustomContainer.this, "javax.swing.plaf.nimbus.NimbusLookAndFeel");
          changeLAF(table, "javax.swing.plaf.nimbus.NimbusLookAndFeel");
          changeLAF(scrollPane, "javax.swing.plaf.nimbus.NimbusLookAndFeel");
          changeLAF((JPanel)table.getModel().getValueAt(0, 0), "javax.swing.plaf.nimbus.NimbusLookAndFeel");
      }
    });
    pH.add(bMetal);
    pH.add(bMotif);
    pH.add(bNimbus);

    JPanel pV = new JPanel();
    pV.setLayout(new BoxLayout(pV, BoxLayout.PAGE_AXIS));
    pV.add(pH);
    pV.add(scrollPane);
    return pV;
  }
  public static void main(String... args) {
    EventQueue.invokeLater(() -> {
      LAF_TableCustomContainer f = new LAF_TableCustomContainer();
      f.getContentPane().add(f.makeUI());
    });
  }
}

class PanelSpinnerRadioButtonData {
  private boolean opt02 = false;
  private Integer from = 0;
  private Integer size = 1;

  PanelSpinnerRadioButtonData() {
    this(false, 5, 10);
  }
  PanelSpinnerRadioButtonData(boolean opt02, Integer from, Integer size) {
    this.opt02 = opt02;
    this.from = from;
    this.size = size;
  }
  public boolean getOption() {
    return opt02;
  }
  public Integer getFrom() {
    return from;
  }
  public Integer getSize() {
    return size;
  }
}

class PanelSpinnerRadioButton extends JPanel {
  public final JRadioButton jrbOption01 = new JRadioButton("01");
  public final JRadioButton jrbOption02 = new JRadioButton("12");
  public final JSpinner jspnValues = new JSpinner(new SpinnerNumberModel(5, 0, 10, 1));

  private final JPanel panel = new JPanel();

  PanelSpinnerRadioButton() {
    this(new PanelSpinnerRadioButtonData(false, 20, 40));
  }

  PanelSpinnerRadioButton(PanelSpinnerRadioButtonData data) {
    super();

    panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
    panel.add(jrbOption01);
    panel.add(jrbOption02);
    panel.add(Box.createRigidArea(new Dimension(5, 0)));
    panel.add(new JSeparator(JSeparator.VERTICAL));
    panel.add(Box.createRigidArea(new Dimension(5, 0)));
    panel.add(jspnValues);
    ButtonGroup bg = new ButtonGroup();
    bg.add(jrbOption01);
    bg.add(jrbOption02);
    ((SpinnerNumberModel) jspnValues.getModel()).setMaximum(data.getSize());
    setData(data);

    init();
  }

  private void init() {
    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
    setBackground(new Color(0, 0, 0, 0));
    add(panel);
  }

  public void setData(PanelSpinnerRadioButtonData data) {
    if (data.getOption()) {
      jrbOption02.setSelected(true);
    } else {
      jrbOption01.setSelected(true);
    }
    ((SpinnerNumberModel) jspnValues.getModel()).setValue(data.getFrom());
  }

  // Used in PSRBTableCellEditor.getCellEditorValue()
  public PanelSpinnerRadioButtonData getData() {
    return new PanelSpinnerRadioButtonData(
             jrbOption02.isSelected(),
             (Integer) ((SpinnerNumberModel) jspnValues.getModel()).getValue(),
             (Integer) ((SpinnerNumberModel) jspnValues.getModel()).getMaximum());
  }
}

class PSRBTableCellRenderer implements TableCellRenderer {
  private final PanelSpinnerRadioButton renderer = new PanelSpinnerRadioButton();
  @Override public Component getTableCellRendererComponent(
      JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    if (value instanceof PanelSpinnerRadioButtonData) {
      renderer.setData((PanelSpinnerRadioButtonData) value);
    }
    return renderer;
  }
}

class PSRBTableCellEditor extends AbstractCellEditor implements TableCellEditor {
  private final PanelSpinnerRadioButton editor = new PanelSpinnerRadioButton();
  @Override public Object getCellEditorValue() {
    return editor.getData();
  }
  @Override public Component getTableCellEditorComponent(
      JTable table, Object value, boolean isSelected, int row, int column) {
    if (value instanceof PanelSpinnerRadioButtonData) {
      editor.setData((PanelSpinnerRadioButtonData) value);
    }
    return editor;
  }
}

当我按下某个按钮时,除了包含我的自定义 JPanel 的 JTable 单元格外,所有 JFrame 都会发生变化。

我试过只强制第一个单元格...

changeLAF((JPanel)table.getModel().getValueAt(0, 0), "javax.swing.plaf.metal.MetalLookAndFeel"); 

但是我得到了异常:

Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: PanelSpinnerRadioButtonData cannot be cast to javax.swing.JPanel
  • SwingUtilities#updateComponentTreeUI(...)方法的局限在于cell渲染器LookAndFeel不能改变
    • 由于 PSRBTableCellRenderer 不扩展 JComponent,因此不受 SwingUtilities # updateComponentTreeUI (...) 方法 LookAndFeel 更新的影响
// @see javax/swing/SwingUtilities.java
public static void updateComponentTreeUI(Component c) {
    updateComponentTreeUI0(c);
    c.invalidate();
    c.validate();
    c.repaint();
}
private static void updateComponentTreeUI0(Component c) {
    if (c instanceof JComponent) {
        JComponent jc = (JComponent) c;
        jc.updateUI();
        JPopupMenu jpm =jc.getComponentPopupMenu();
        if(jpm != null) {
            updateComponentTreeUI(jpm);
        }
    }
    Component[] children = null;
    if (c instanceof JMenu) {
        children = ((JMenu)c).getMenuComponents();
    }
    else if (c instanceof Container) {
        children = ((Container)c).getComponents();
    }
    if (children != null) {
        for (Component child : children) {
            updateComponentTreeUI0(child);
        }
    }
}

您可以通过覆盖 JTable#updateUI() 方法并重新创建单元格渲染器和编辑器来避免这种情况。

JTable table = new JTable(new DefaultTableModel(objectMatrix, hdrsObjects)) {
  @Override public void updateUI() {
    super.updateUI();
    setRowHeight(30);
    TableColumn tc = getColumn("PanelSpinnerRadioButton Class Column");
    tc.setCellRenderer(new PSRBTableCellRenderer());
    tc.setCellEditor(new PSRBTableCellEditor());
  }
};

LAF_TableCustomContainer2.java

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;

public class LAF_TableCustomContainer2 extends JFrame {
  public LAF_TableCustomContainer2()  {
    setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    setSize(300, 300);
    setVisible(true);
    setLocationRelativeTo(null);
  }

  public static void changeLAF(Container container, String laf) {
    try {
      UIManager.setLookAndFeel(laf);
    } catch (ClassNotFoundException | InstantiationException
           | IllegalAccessException | UnsupportedLookAndFeelException e) {
    }
    SwingUtilities.updateComponentTreeUI(container);
  }

  static final JFrame frame = new JFrame();

  public JComponent makeUI() {
    JPanel pV = new JPanel();
      pV.setLayout(new BoxLayout(pV, BoxLayout.PAGE_AXIS));

    String[] hdrsObjects = {"PanelSpinnerRadioButton Class Column"};
    Object[][] objectMatrix = new Object[3][1];
    objectMatrix[0][0] = new PanelSpinnerRadioButtonData(false, 10, 40);
    objectMatrix[1][0] = new PanelSpinnerRadioButtonData(true,  20, 40);
    objectMatrix[2][0] = new PanelSpinnerRadioButtonData(false, 30, 40);

      JTable table = new JTable(new DefaultTableModel(objectMatrix, hdrsObjects)) {
          @Override public void updateUI() {
              super.updateUI();
              setRowHeight(30);
              TableColumn tc = getColumn("PanelSpinnerRadioButton Class Column");
              tc.setCellRenderer(new PSRBTableCellRenderer());
              tc.setCellEditor(new PSRBTableCellEditor());
          }
      };
    JScrollPane scrollPane = new JScrollPane(table);
    scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
    scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
//     table.setRowHeight(30);
//     TableColumn tc = table.getColumn("PanelSpinnerRadioButton Class Column");
//     tc.setCellRenderer(new PSRBTableCellRenderer());
//     tc.setCellEditor(new PSRBTableCellEditor());

    JPanel pH = new JPanel();
    pH.setLayout(new BoxLayout(pH, BoxLayout.LINE_AXIS));

    JButton bMetal = new JButton("Metal");
    bMetal.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent evt) {
        changeLAF(pV, "javax.swing.plaf.metal.MetalLookAndFeel");
      }
    });

    JButton bMotif = new JButton("Motif");
    bMotif.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent evt) {
        changeLAF(pV, "com.sun.java.swing.plaf.motif.MotifLookAndFeel");
      }
    });

    JButton bNimbus = new JButton("Nimbus");
    bNimbus.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent evt) {
        changeLAF(pV, "javax.swing.plaf.nimbus.NimbusLookAndFeel");
      }
    });
    pH.add(bMetal);
    pH.add(bMotif);
    pH.add(bNimbus);

    pV.add(pH);
    pV.add(scrollPane);
    return pV;
  }
  public static void main(String... args) {
    EventQueue.invokeLater(() -> {
      LAF_TableCustomContainer2 f = new LAF_TableCustomContainer2();
      f.getContentPane().add(f.makeUI());
    });
  }
}

class PanelSpinnerRadioButtonData {
  private boolean opt02 = false;
  private Integer from = 0;
  private Integer size = 1;

  PanelSpinnerRadioButtonData() {
    this(false, 5, 10);
  }
  PanelSpinnerRadioButtonData(boolean opt02, Integer from, Integer size) {
    this.opt02 = opt02;
    this.from = from;
    this.size = size;
  }
  public boolean getOption() {
    return opt02;
  }
  public Integer getFrom() {
    return from;
  }
  public Integer getSize() {
    return size;
  }
}

class PanelSpinnerRadioButton extends JPanel {
  public final JRadioButton jrbOption01 = new JRadioButton("01");
  public final JRadioButton jrbOption02 = new JRadioButton("12");
  public final JSpinner jspnValues = new JSpinner(new SpinnerNumberModel(5, 0, 10, 1));

  private final JPanel panel = new JPanel();

  PanelSpinnerRadioButton() {
    this(new PanelSpinnerRadioButtonData(false, 20, 40));
  }

  PanelSpinnerRadioButton(PanelSpinnerRadioButtonData data) {
    super();

    panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
    panel.add(jrbOption01);
    panel.add(jrbOption02);
    panel.add(Box.createRigidArea(new Dimension(5, 0)));
    panel.add(new JSeparator(JSeparator.VERTICAL));
    panel.add(Box.createRigidArea(new Dimension(5, 0)));
    panel.add(jspnValues);
    ButtonGroup bg = new ButtonGroup();
    bg.add(jrbOption01);
    bg.add(jrbOption02);
    ((SpinnerNumberModel) jspnValues.getModel()).setMaximum(data.getSize());
    setData(data);

    init();
  }

  private void init() {
    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
    setBackground(new Color(0, 0, 0, 0));
    add(panel);
  }

  public void setData(PanelSpinnerRadioButtonData data) {
    if (data.getOption()) {
      jrbOption02.setSelected(true);
    } else {
      jrbOption01.setSelected(true);
    }
    ((SpinnerNumberModel) jspnValues.getModel()).setValue(data.getFrom());
  }

  // Used in PSRBTableCellEditor.getCellEditorValue()
  public PanelSpinnerRadioButtonData getData() {
    return new PanelSpinnerRadioButtonData(
             jrbOption02.isSelected(),
             (Integer)((SpinnerNumberModel) jspnValues.getModel()).getValue(),
             (Integer)((SpinnerNumberModel) jspnValues.getModel()).getMaximum());
  }
}

class PSRBTableCellRenderer implements TableCellRenderer {
  private final PanelSpinnerRadioButton renderer = new PanelSpinnerRadioButton();
  @Override public Component getTableCellRendererComponent(
    JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    if (value instanceof PanelSpinnerRadioButtonData) {
      renderer.setData((PanelSpinnerRadioButtonData) value);
    }
    return renderer;
  }
}

class PSRBTableCellEditor extends AbstractCellEditor implements TableCellEditor {
  private final PanelSpinnerRadioButton editor = new PanelSpinnerRadioButton();
  @Override public Object getCellEditorValue() {
    return editor.getData();
  }
  @Override public Component getTableCellEditorComponent(
    JTable table, Object value, boolean isSelected, int row, int column) {
    if (value instanceof PanelSpinnerRadioButtonData) {
      editor.setData((PanelSpinnerRadioButtonData) value);
    }
    return editor;
  }
}