I'm trying to draw lines over my JTable using a Painter object that I've made, but for some reason table.getGraphics()
returns null.
Painter class:
import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JTable;
public class Painter extends JTable {
public Painter(){
}
public void paintSudokuLines(Graphics g){
paintComponent(g);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(3));
g2.drawLine(0, 300, 400, 250);
}
}
I'm calling the method with:
private Painter paint = new Painter();
paint.paintSudokuLines(table.getGraphics());
I have no idea why this is the case, so I need some explanation.
Never use getGraphics
, it can return null
(as you've found) and isn't how custom painting should be done. You should NEVER have a need to call paintComponent
yourself. If when printing, you wouldn't call it directly, at the best, you'd break the paint chain and introduce no end of additional issues and artifacts.
For a better understanding of how painting works see Painting in AWT and Swing.
Now, let me give you a quick demonstration of why you shouldn't try and paint over the top of a JTable
like you're trying to do...
Now, don't get me wrong, I'm sure with some very clever deduction work you could calculate the row/column positions so as to be able to paint the guides properly, but that's a lot of extra work.
A better solution might be to use a custom TableCellRenderer
...
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
import javax.swing.border.MatteBorder;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
public class SudokuTest {
public static void main(String[] args) {
new SudokuTest();
}
public SudokuTest() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(new SudokuTable()));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class SudokuTable extends JTable {
public SudokuTable() {
super(new DefaultTableModel(9, 9));
setDefaultRenderer(Object.class, new SudokuCellRender());
}
}
public class SudokuCellRender extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
int left = 0;
int top = 0;
int right = 0;
int bottom = 0;
if ((column % 3) == 0) {
left = 2;
} else if ((column % 3) == 2) {
right = 2;
}
if ((row % 3) == 0) {
top = 2;
}
if ((row % 3) == 2) {
bottom = 2;
}
setBorder(new MatteBorder(top, left, bottom, right, Color.RED));
return this;
}
}
}
Now, this is just a proof of concept. You may need to use a compound border to offset the cell property (so all cells have at least a 2 pixel border around them and/or reduce the border width ;))
Also, take a look at How to Use Tables for more details