我有从 JPanel 扩展的自定义组件。
名称为:PanelButton
和 PanelSlider
问题 1:
使用非均匀矩阵创建 xxxTableModel(...)
是否有效(或安全)?
String[] hdrsObjects = {"PanelButton Class", "PanelSlider Class"};
Object[][] objectMatrix = new Object[3][2];
objectMatrix[0][0] = new PanelButtonData(...);
objectMatrix[1][0] = new PanelButtonData(...);
objectMatrix[2][0] = new PanelButtonData(...);
// objectMatrix[0][1] = /*Non Assigned*/
objectMatrix[1][1] = new PanelSliderData(0, 20, 40);
objectMatrix[2][1] = new PanelSliderData(30, 40, 60);
JTable Mytable = new JTable(new MyTableModel(objectMatrix, hdrsObjects)) {...}
这相当于 3 行不同长度:
jtblGeneral.setModel(new DefaultTableModel(
new Object [][] { {"Cell Row:0,Col:0"}, {"Cell Row:1,Col:0", "Cell Row:1,Col:1"}, {"Cell Row:2,Col:0", "Cell Row:2,Col:1"}
},
new String [] {
"Title 1", "Title 2"
}
));
现在我正在实现我自己的TableCellRenderer
class MyTableCellRenderer implements TableCellRenderer {
private final PanelSlider ps = new PanelSlider(new PanelSliderData(0, 25, 50));
private final PanelButton pb = new PanelButton(new PanelButtonData(false));
@Override public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value instanceof PanelButtonData) {
pb.setData((PanelButtonData) value);
return pb;
}
if (value instanceof PanelSliderData) {
ps.setData((PanelSliderData) value);
return ps;
}
//if (value != null)
// return (Component) value;
//return this;
//return null;
// return table.getDefaultRenderer(String.class).getTableCellRendererComponent(
// table, value, isSelected, hasFocus, row, column);
return new JLabel();
}
}
问题 2:
如果之前问题的答案为"is"。当我的自定义 JPanel 类中的值为 null 且未定义(如 row:0、col:1 中的单元格)时,我必须返回什么类型对象?
return null;
当我返回 null 时(Nimbus 和 GTK 外观和感觉会出现问题)UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
或UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
这里是异常java.lang.NullPointerException:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at javax.swing.plaf.synth.SynthTableUI.paintCell(SynthTableUI.java:684)
at javax.swing.plaf.synth.SynthTableUI.paintCells(SynthTableUI.java:580)
at javax.swing.plaf.synth.SynthTableUI.paint(SynthTableUI.java:364)
at javax.swing.plaf.synth.SynthTableUI.update(SynthTableUI.java:275)
at javax.swing.JComponent.paintComponent(JComponent.java:780)
at javax.swing.JComponent.paint(JComponent.java:1056)
返回新的 JLabel();
返回新的Component();
问题 3:
但是,当值不为 null 并且我不知道 Class 类型时,如何处理返回?
if (value != null) return (Component) value;
return table.getDefaultRenderer(String.class).getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
- 扩展我的
Component
的MyTableCellRenderer
类并返回它;
所有代码
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
public class TableButtonSlider extends JFrame {
public TableButtonSlider() {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setSize(600, 300);
setVisible(true);
setLocationRelativeTo(null);
}
public static void setLAF(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 = {"PanelButton Class", "PanelSlider Class"};
Object[][] objectMatrix = new Object[3][2];
objectMatrix[0][0] = new PanelButtonData(true);
objectMatrix[1][0] = new PanelButtonData(false);
objectMatrix[2][0] = new PanelButtonData(false);
// objectMatrix[0][1] = new PanelSliderData(10, 30, 40);
objectMatrix[1][1] = new PanelSliderData(0, 20, 40);
objectMatrix[2][1] = new PanelSliderData(30, 40, 60);
JTable Mytable = new JTable(new MyTableModel(objectMatrix, hdrsObjects)) {
@Override public void updateUI() {
super.updateUI();
setRowHeight(30);
TableColumn tc;
tc = getColumn("PanelSlider Class");
tc.setCellRenderer(new MyTableCellRenderer());
tc.setCellEditor(new MyTableCellEditor());
tc = getColumn("PanelButton Class");
tc.setCellRenderer(new MyTableCellRenderer());
tc.setCellEditor(new MyTableCellEditor());
}
};
JScrollPane scrollPane = new JScrollPane(Mytable);
JPanel pH = new JPanel();
pH.setLayout(new BoxLayout(pH, BoxLayout.LINE_AXIS));
JPanel pV = new JPanel();
pV.setLayout(new BoxLayout(pV, BoxLayout.PAGE_AXIS));
JButton bInsert = new JButton("Insert");
bInsert.addActionListener((ActionEvent e) -> {
((MyTableModel)Mytable.getModel()).addRow(
new Object[] {
new PanelButtonData(false),
new PanelSliderData(0, 25, 50)
}
);
Mytable.updateUI();
});
JButton bMetal = new JButton("Metal");
bMetal.addActionListener((ActionEvent) -> {
setLAF(TableButtonSlider.this, "javax.swing.plaf.metal.MetalLookAndFeel");
});
JButton bMotif = new JButton("Motif");
bMotif.addActionListener((ActionEvent) -> {
setLAF(TableButtonSlider.this, "com.sun.java.swing.plaf.motif.MotifLookAndFeel");
});
JButton bNimbus = new JButton("Nimbus");
bNimbus.addActionListener((ActionEvent) -> {
setLAF(TableButtonSlider.this, "javax.swing.plaf.nimbus.NimbusLookAndFeel");
});
JButton bMacOS = new JButton("mac");
bMacOS.addActionListener((ActionEvent evt) -> {
setLAF(TableButtonSlider.this, "com.apple.laf.AquaLookAndFeel");
});
JButton bWindows = new JButton("win");
bWindows.addActionListener((ActionEvent) -> {
setLAF(TableButtonSlider.this, "com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
});
JButton bLinux = new JButton("lnx");
bLinux.addActionListener((ActionEvent) -> {
setLAF(TableButtonSlider.this, "com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
});
pH.add(bInsert);
pH.add(Box.createRigidArea(new Dimension(1,0)));
pH.add(new JSeparator(JSeparator.VERTICAL));
pH.add(Box.createRigidArea(new Dimension(1,0)));
pH.add(bLinux);
pH.add(bMacOS);
pH.add(bWindows);
pH.add(Box.createRigidArea(new Dimension(1,0)));
pH.add(new JSeparator(JSeparator.VERTICAL));
pH.add(Box.createRigidArea(new Dimension(1,0)));
pH.add(bMetal);
pH.add(bMotif);
pH.add(bNimbus);
pV.add(pH);
pV.add(scrollPane);
return pV;
}
public static void main(String... args) {
UIManager.put("Slider.paintValue", false);
EventQueue.invokeLater(() -> {
TableButtonSlider f = new TableButtonSlider();
f.getContentPane().add(f.makeUI());
});
}
}
class PanelButton extends JPanel {
JButton jbtWavRow = new JButton();
private final JPanel panel = new JPanel();
PanelButton(PanelButtonData data) {
super();
panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
panel.add(Box.createRigidArea(new Dimension(2,0)));
panel.add(jbtWavRow);
panel.add(Box.createRigidArea(new Dimension(2,0)));
jbtWavRow.setFont(new Font("Monospaced", Font.PLAIN, 10));
setData(data);
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
add(panel);
}
public PanelButtonData getData() {
return new PanelButtonData(jbtWavRow.getActionCommand().equals("+"));
}
public void setData(PanelButtonData data) {
for (ActionListener al : jbtWavRow.getActionListeners()) {
jbtWavRow.removeActionListener(al);
}
if(data.getIns()) {
jbtWavRow.setText("Insert");
jbtWavRow.setActionCommand("+");
jbtWavRow.addActionListener((ActionEvent e) -> {
JTable table = (JTable)SwingUtilities.getAncestorOfClass(
JTable.class, (Component) e.getSource());
table.getCellEditor().stopCellEditing();
((MyTableModel)table.getModel()).addRow(
new Object[] {
new PanelButtonData(false),
new PanelSliderData(0, 25, 50)
}
);
table.updateUI();
});
} else {
jbtWavRow.setText("Remove");
jbtWavRow.setActionCommand("-");
jbtWavRow.addActionListener((ActionEvent e) -> {
JTable table = (JTable) SwingUtilities.getAncestorOfClass(
JTable.class, (Component) e.getSource());
int row = table.getEditingRow();
table.getCellEditor().stopCellEditing();
((MyTableModel) table.getModel()).removeRow(row);
// table.updateUI();
});
}
}
}
class PanelButtonData {
private boolean add = false;
PanelButtonData(Boolean add) { this.add = add; }
public void setIns(Boolean add) { this.add = add; }
public boolean getIns() { return add; }
}
class PanelSlider extends JPanel {
private final JSlider jslChanger = new JSlider(SwingConstants.HORIZONTAL);
private final JPanel panel = new JPanel();
PanelSlider(PanelSliderData data) {
super();
panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
panel.add(Box.createRigidArea(new Dimension(2,0)));
panel.add(jslChanger);
panel.add(Box.createRigidArea(new Dimension(2,0)));
setData(data);
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
add(panel);
}
public void setData(PanelSliderData data) {
jslChanger.setMinimum(data.getMin());
jslChanger.setValue(data.getVal());
jslChanger.setMaximum(data.getMax());
}
// Used in MyTableCellRenderer.getCellEditorValue()
public PanelSliderData getData() {
return new PanelSliderData(jslChanger.getMinimum(), jslChanger.getValue(), jslChanger.getMaximum());
}
}
class PanelSliderData {
private Integer Min = 0;
private Integer Val = 25;
private Integer Max = 50;
PanelSliderData(int Min, int Val, int Max) {
this.Min = Min;
this.Val = Val;
this.Max = Max;
}
public Integer getMin() { return Min; }
public Integer getVal() { return Val; }
public Integer getMax() { return Max; }
}
class MyTableCellRenderer implements TableCellRenderer {
private final PanelSlider ps = new PanelSlider(new PanelSliderData(0, 25, 50));
private final PanelButton pb = new PanelButton(new PanelButtonData(false));
@Override public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value instanceof PanelButtonData) {
pb.setData((PanelButtonData) value);
return pb;
}
if (value instanceof PanelSliderData) {
ps.setData((PanelSliderData) value);
return ps;
}
//if (value != null)
// return (Component) value;
//return this;
return null;
//return table.getDefaultRenderer(String.class).getTableCellRendererComponent(
// table, value, isSelected, hasFocus, row, column);
//return new JLabel();
}
}
class MyTableCellEditor extends AbstractCellEditor implements TableCellEditor {
protected Object output;
private final PanelButton pb = new PanelButton(new PanelButtonData(false));
private final PanelSlider ps = new PanelSlider(new PanelSliderData(0, 25, 50));
@Override public Object getCellEditorValue() {
if (output instanceof PanelButton) {
return pb.getData();
}
if (output instanceof PanelSlider) {
return ps.getData();
}
return null;
}
@Override public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column) {
if (value instanceof PanelButtonData) {
pb.setData((PanelButtonData) value);
output = pb;
return pb;
}
if (value instanceof PanelSliderData) {
ps.setData((PanelSliderData) value);
output = ps;
return ps;
}
return null;
}
}
class MyTableModel extends AbstractTableModel {
//class MyTableModel extends DefaultTableModel {
private Object[][] data;
private Object[] columns;
public MyTableModel(Object[][] data, Object[] columns) {
this.data = data;
this.columns = columns;
}
@Override public Object getValueAt(int rowIndex, int columnIndex) {
if (data != null) {
if (data.length > 0) {
return data[rowIndex][columnIndex];
}
}
return null;
}
@Override public int getColumnCount() {
return ((columns == null) ? 0: columns.length);
}
@Override public int getRowCount() {
return ((data == null) ? 0: data.length);
}
@Override public Class getColumnClass(int columnIndex) {
if (data != null) {
if (data.length > 0) {
if (data[0][columnIndex] instanceof PanelButton) {
return PanelButton.class;
}
if (data[0][columnIndex] instanceof PanelSlider) {
return PanelSlider.class;
}
//return data[0][columnIndex].getClass();
return String.class;
}
}
return Object.class;
}
@Override public boolean isCellEditable(int rowIndex, int columnIndex) {
return true;
}
@Override public void setValueAt(Object value, int row, int col) {
data[row][col] = value;
fireTableCellUpdated(row, col);
}
@Override public String getColumnName(int columnIndex) {
return (String)columns[columnIndex];
}
//@Override
public void removeRow(int row) {
Object[][] newData = new Object[data.length - 1][data[0].length];
int rown = 0;
for (int row1 = 0; row1 <data.length; row1++) {
if (row1 != row) {
for (int col = 0; col < data[0].length; col++) {
newData[rown][col] = data[row1][col];
}
rown++;
}
}
data = newData;
}
//@Override
public void addRow(Object[] rowData) {
Object[][] newData;
int maxCol;
if ((data != null) && (data.length > 0)) {
newData = new Object[data.length + 1][data[0].length];
for (int row = 0; row <data.length; row++) {
for (int col = 0; col < data[0].length; col++) {
newData[row][col] = data[row][col];
}
}
maxCol = data[0].length < rowData.length?data[0].length:rowData.length;
} else {
newData = new Object[1][rowData.length];
maxCol = rowData.length;
}
//Insert rowData objects
for (int col = 0; col < maxCol; col++) {
newData[newData.length - 1][col] = rowData[col];
}
data = newData;
}
}
编辑 1
public static void main(String... args) {
UIManager.put("Slider.paintValue", false);
try {
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException | UnsupportedLookAndFeelException e) { }
EventQueue.invokeLater(() -> {
TableButtonSlider f = new TableButtonSlider();
f.getContentPane().add(f.makeUI());
});
}
建立nimbus
LookAndFeel,就像在main
方法上的第一个操作一样,引发异常:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at javax.swing.plaf.synth.SynthTableUI.paintCell(SynthTableUI.java:684)
at javax.swing.plaf.synth.SynthTableUI.paintCells(SynthTableUI.java:580)
at javax.swing.plaf.synth.SynthTableUI.paint(SynthTableUI.java:364)
at javax.swing.plaf.synth.SynthTableUI.update(SynthTableUI.java:275)
at javax.swing.JComponent.paintComponent(JComponent.java:780)
at javax.swing.JComponent.paint(JComponent.java:1056)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JViewport.paint(JViewport.java:728)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JLayeredPane.paint(JLayeredPane.java:586)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5217)
at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1579)
at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1502)
at javax.swing.RepaintManager.paint(RepaintManager.java:1272)
at javax.swing.JComponent.paint(JComponent.java:1042)
at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:39)
at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:79)
at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:116)
at java.awt.Container.paint(Container.java:1975)
at java.awt.Window.paint(Window.java:3904)
at javax.swing.RepaintManager$4.run(RepaintManager.java:842)
at javax.swing.RepaintManager$4.run(RepaintManager.java:814)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:814)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:789)
at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:738)
at javax.swing.RepaintManager.access$1200(RepaintManager.java:64)
at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1732)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
最佳答案
Is it valid (or secure) to create a xxxTableModel(...) with nonuniform Matrix?
是的,您可以做到这一点,但是您需要付出很多努力才能使其工作,因为 JTable
确实不是为这种方式设计的
If Answer is YES for before Question. When the value is null and not defined (like cell in row:0, col:1) in my custom JPanel Classes, what Type Object I must return?
您必须返回 Component
的实例,不能返回 null
But, When the value Is not null and I don't know the Class type, How handle the return?
这与你的第二个问题的答案相同,你必须返回一个 Component
实例,这取决于你想要如何处理 null
之间的差异和“未知”值
观察结果
深入研究代码后,您对 updateUI
进行了不适当的调用,这并不是在更改 UI 状态时触发 UI“更新”的方法,它只是为了(由系统)用来通知组件外观委托(delegate)已更改
在您的 TableModel
中,您应该在模型更新时触发适当的事件,这将通知父 JTable
并进行适当的更改。
我删除了代码中对 updateUI
的所有调用,只是更新了 addRow
方法来调用 fireTableRowsInserted
public void addRow(Object[] rowData) {
Object[][] newData;
int maxCol;
if ((data != null) && (data.length > 0)) {
newData = new Object[data.length + 1][data[0].length];
for (int row = 0; row < data.length; row++) {
for (int col = 0; col < data[0].length; col++) {
newData[row][col] = data[row][col];
}
}
maxCol = data[0].length < rowData.length ? data[0].length : rowData.length;
} else {
newData = new Object[1][rowData.length];
maxCol = rowData.length;
}
//Insert rowData objects
for (int col = 0; col < maxCol; col++) {
newData[newData.length - 1][col] = rowData[col];
}
data = newData;
fireTableRowsInserted(data.length - 1, data.length - 1);
}
并且NullPointerException
消失了。您还必须对 removeRow
进行适当的更改
关于java - 具有非均匀对象数组的 JTable,自定义 TableCellRenderer 实现的返回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45045025/