I'm trying to implement a method to paint a specific portion of a grid. To do so I overrided the paintComponent method:
public class Frame extends JPanel {
...
#Override
public void paintComponent( Graphics g ) {
super.paintComponent( g );
g.clearRect(0, 0, getWidth(), getHeight());
this.rectWidth = getWidth() / this.NUM_COLUMNS;
this.rectHeight = getHeight() / this.NUM_ROWS;
for (int i = 0; i < NUM_ROWS; i++) {
for (int j = 0; j < NUM_COLUMNS; j++) {
int x = i * this.rectWidth;
int y = j * this.rectHeight;
g.setColor( Color.WHITE );
g.fillRect( x, y, this.rectWidth, this.rectHeight );
}
}
}
}
Which is fine but, then I want to paint specific portions on a function call like this:
public void specificPaint( int coordinateX, int coordinateY, Color color ){
Graphics g = this.getGraphics();
int x = coordinateX * this.rectWidth;
int y = coordinateY * this.rectHeight;
g.setColor( color );
g.fillRect( x, y, this.rectWidth, this.rectWidth);
}
The call should be like
// TESTING
this.modelMap.specificPaint( 40,40,Color.RED );
this.modelMap.specificPaint( 10,10,Color.RED );
this.modelMap.specificPaint( 20,25,Color.BLUE );
I'm getting a null pointer error with the Graphics object, why can't I recover and use it?
Is there a better approach?
Never do this when getting a Graphics object from a component:
Graphics g = this.getGraphics();
The Graphics object thus obtained will not be long-lived, and this can result in either NPE (like you're getting) or an image that does not persist on repaints. Instead, do your drawing within the paintComponent method. Note that you can call repaint(...) and specify a particular Rectangle to paint by passing in a Rectangle parameter.
Note that you can call getGraphics() on a BufferedImage and draw to it, and then draw the BufferedImage within your paintComponent method if you want to do spot drawing that does not move.
An unrelated recommendation: Avoid calling your class Frame as this can result in a name clash with the java.awt.Frame class if you're not careful.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class MyPanel extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private int cols;
private int rows;
private static final Color BG = Color.BLACK;
private static final int GAP = 1;
private BufferedImage img;
private int rectWidth;
private int rectHeight;
public MyPanel(int rows, int cols) {
this.cols = cols;
this.rows = rows;
img = createMyImage();
}
public void specificPaint(int coordinateX, int coordinateY, Color color) {
Graphics g = img.getGraphics(); // get img's Graphics object
int x = coordinateX * this.rectWidth + GAP;
int y = coordinateY * this.rectHeight + GAP;
g.setColor(color);
g.fillRect(x, y, rectWidth - 2 * GAP, rectWidth - 2 * GAP);
g.dispose();
repaint();
}
private BufferedImage createMyImage() {
img = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setBackground(BG);
g2.clearRect(0, 0, img.getWidth(), img.getHeight());
this.rectWidth = img.getWidth() / cols;
this.rectHeight = img.getHeight() / rows;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
int x = i * this.rectWidth + GAP;
int y = j * this.rectHeight + GAP;
g2.setColor(Color.WHITE);
g2.fillRect(x, y, this.rectWidth - 2 * GAP, this.rectHeight - 2 * GAP);
}
}
g2.dispose();
return img;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, this);
}
// if you need to draw changing non-static images, do it here
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet() || img == null) {
return super.getPreferredSize();
}
int w = img.getWidth();
int h = img.getHeight();
return new Dimension(w, h);
}
private static void createAndShowGui() {
MyPanel modelMap = new MyPanel(50, 50);
JFrame frame = new JFrame("MyPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(modelMap);
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
modelMap.specificPaint( 40,40,Color.RED );
modelMap.specificPaint( 10,10,Color.RED );
modelMap.specificPaint( 20,25,Color.BLUE );
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Change
public void specificPaint(int coordinateX, int coordinateY, Color color)
to
public void specificPaint(int coordinateX, int coordinateY, Color color, Graphics g)
Then call specificPaint inside your paintComponent method with the Graphics object you are drawing with
See Hovercraft's answer for more details as to why using this.getGraphics() doesn't work
Related
So I've added a grid to my BufferedImage, by doing it in the PaintComponentlike this. It works perfect.
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Color c=new Color(184, 184, 184, 255);
g.drawImage(canvas, 0, 0, this);
for ( int x = 0; x <= getWidth(); x += 10 ){
for ( int y = 0; y <= getHeight(); y += 10 ){
g.setColor(c);
g.drawRect( x, y, 10, 10 );
}
}
}
BUT, when I then draw my line/circles in this function:
public void drawPixels(Object val_x, Boolean highlight)
{
String[] values = val_x.toString().replaceAll("[()]", "").split(",");
if (highlight){
canvas.setRGB(Integer.parseInt(values[0]), Integer.parseInt(values[1]), Color.RED.getRGB());
}else{
canvas.setRGB(Integer.parseInt(values[0]), Integer.parseInt(values[1]), c.getRGB());
}
repaint();
}
I see that some of my pixels is overwriten by the grid from paintComponent.
Is there a way to z-index the grid behind the pixels I draw, or maybe draw the grid first, and then draw on the top of that?
My GUI looks like this, where you can see that my circles pixels is swapped with the grid:
If you want to look at the whole drawing .java file
package DrawCanvas;
import com.sun.org.apache.xpath.internal.operations.Bool;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
public class DrawCanvas extends JPanel {
private BufferedImage canvas;
private static Graphics g2;
final private Color c = Color.BLACK;
private final static Color def_bg = new Color(108, 108, 108, 255);
//private final static Color def_bg = new Color(80, 80, 80, 255);
int width = 1280;
int height = 720;
public DrawCanvas() {
canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
g2 = canvas.getGraphics();
fillCanvas(def_bg);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(canvas.getWidth(), canvas.getHeight());
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Color c=new Color(184, 184, 184, 255);
g.drawImage(canvas, 0, 0, this);
for ( int x = 0; x <= getWidth(); x += 10 ){
for ( int y = 0; y <= getHeight(); y += 10 ){
g.setColor(c);
g.drawRect( x, y, 10, 10 );
}
}
}
public void fillCanvas(Color c) {
int color = c.getRGB();
for (int x = 0; x < canvas.getWidth(); x++) {
for (int y = 0; y < canvas.getHeight(); y++) {
canvas.setRGB(x, y, color);
}
}
repaint();
}
// Implementation from Wikipedia: https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm#All_cases
public void drawPixels(Object val_x, Boolean highlight)
{
String[] values = val_x.toString().replaceAll("[()]", "").split(",");
if (highlight){
canvas.setRGB(Integer.parseInt(values[0]), Integer.parseInt(values[1]), Color.RED.getRGB());
}else{
canvas.setRGB(Integer.parseInt(values[0]), Integer.parseInt(values[1]), c.getRGB());
}
repaint();
}
public void clearImage() {
Graphics2D g2 = (Graphics2D) canvas.getGraphics();
g2.setBackground(def_bg);
g2.clearRect(0,0, (int)canvas.getWidth(), (int)canvas.getHeight());
repaint();
}
}
EDIT 1 solution
package DrawCanvas;
import com.sun.org.apache.xpath.internal.operations.Bool;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
public class DrawCanvas extends JPanel {
private BufferedImage canvas;
private static Graphics g2;
final private Color c = Color.BLACK;
private final static Color def_bg = new Color(108, 108, 108, 255);
private final static Color grid = new Color(149, 149, 149, 186);
//private final static Color def_bg = new Color(80, 80, 80, 255);
int width = 1280;
int height = 720;
public DrawCanvas() {
canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
g2 = canvas.getGraphics();
fillCanvas(def_bg);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(canvas.getWidth(), canvas.getHeight());
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(canvas, 0, 0, this);
}
public void fillCanvas(Color c) {
int color = c.getRGB();
for (int x = 0; x < canvas.getWidth(); x++) {
for (int y = 0; y < canvas.getHeight(); y++) {
canvas.setRGB(x, y, color);
}
}
drawGrid();
repaint();
}
// Implementation from Wikipedia: https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm#All_cases
// Implementation from Wikipedia: https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm#All_cases
public void drawPixels(Object val_x, Boolean highlight)
{
Graphics2D graph = canvas.createGraphics();
String[] values = val_x.toString().replaceAll("[()]", "").split(",");
if (highlight){
graph.setColor(Color.RED);
graph.drawOval(Integer.parseInt(values[0]), Integer.parseInt(values[1]), 1, 1);
}else{
graph.setColor(c);
graph.drawOval(Integer.parseInt(values[0]), Integer.parseInt(values[1]), 1, 1);
}
}
public void drawGrid(){
Graphics2D graph = canvas.createGraphics();
for (int x = 0; x < canvas.getWidth(); x += 10) {
for (int y = 0; y < canvas.getHeight(); y += 10) {
graph.setColor(grid);
graph.drawRect( x, y, 10, 10 );
}
}
repaint();
}
public void clearImage() {
Graphics2D g2 = (Graphics2D) canvas.getGraphics();
g2.setBackground(def_bg);
g2.clearRect(0,0, (int)canvas.getWidth(), (int)canvas.getHeight());
drawGrid();
repaint();
}
}
I have inefficient code of a square wave. I have 2 buttons, 1 table and something like a coordinate system where the square appears in. I want the wave to scroll/move in real time until it hits the end of the coordinate system instead of just appearing by selecting both of the buttons. Additionally, if anyone has a better way of drawing a square wave please tell me.
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawLine(20, 300, 20, 450);
g2d.drawLine(20, 350, 400, 350);
g2d.drawLine(20, 400, 400, 400);
g2d.drawLine(20, 450, 400, 450);
if (this.jButtonSTART.isSelected() & this.jButtonAND.isSelected()) {
this.draw(g2d);
}
}
public void draw(Graphics2D g2d) {
boolean up = true;
while (x <= 380) {
g2d.setColor(Color.blue);
if (x > 0 && x % 95 == 0) {
up = !up;
g2d.drawLine(20 + x, up ? 315 : 350 + y, 20 + x, up ? 350 : 315 + y);
} else {
if (up) {
g2d.drawLine(20 + x, 315 + y, 21 + x, y + 315);
} else {
g2d.drawLine(20 + x, 350 + y, 21 + x, y + 350);
}
}
x++;
}
x = 0;
}
Simple way to draw your square wave and move it:
Create a BufferedImage that is longer than your GUI. It should have length that matches a the period of your square wave and be at least twice as long as the GUI component that it's displayed in.
Draw within the paintComponent method override of a JPanel, not the paint method.
Call the super's paintComponent method first within your override.
You'll draw the image using g.drawImage(myImage, imageX, imageY, this) where imageX and imageY are private instance fields of the JPanel-extending drawing class.
In a Swing Timer, advance imageX with each tick of the Timer, that is each time its ActionListener's actionPerformed method is called).
Then call repaint() on the drawing JPanel within the same actionPerformed method.
Done.
for example, note that this code does not do exactly what you're trying to do, but does show an example of Swing animation using a Swing Timer and paintComponent.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class MoveWave extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = 200;
private static final int TIMER_DELAY = 40;
public static final int DELTA_X = 2;
public static final int STARTING_MY_IMAGE_X = -PREF_W;
private static final Color COLOR_1 = Color.RED;
private static final Color COLOR_2 = Color.BLUE;
private static final Color BG = Color.BLACK;
private static final int CIRCLE_COUNT = 10;
private BufferedImage myImage = null;
private int myImageX = STARTING_MY_IMAGE_X;
private int myImageY = 0;
public MoveWave() {
setBackground(BG);
myImage = new BufferedImage(2 * PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = myImage.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(new GradientPaint(0, 0, COLOR_1, 20, 20, COLOR_2, true));
for (int i = 0; i < CIRCLE_COUNT; i++) {
int x = (i * 2 * PREF_W) / CIRCLE_COUNT;
int y = PREF_H / 4;
int width = (2 * PREF_W) / CIRCLE_COUNT;
int height = PREF_H / 2;
g2.fillOval(x, y, width, height);
}
g2.dispose();
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (myImage != null) {
g.drawImage(myImage, myImageX, myImageY, this);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
myImageX += DELTA_X;
if (myImageX >= 0) {
myImageX = STARTING_MY_IMAGE_X;
}
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("MoveWave");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new MoveWave());
frame.setResizable(false);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
I am trying to create a generic node that can modified and to ones wishes but I have run in some trouble because my skills in Swing and such is not so good.
Problem 1: I can't seem to get two Node/Jpanels in the same JFrame.
lesser Problem 2: The stroke is cut off from the JPanel border (Should I build in a kind of bleed so this will not happen?)
Any aid is helpful :)
public class SimpleGui {
public SimpleGui() {
JFrame frame = new JFrame();
NodePanel panel = new NodePanel(3, 2);
panel.setLayout(new javax.swing.SpringLayout());
frame.add(panel);
NodePanel panel2 = new NodePanel(3, 2);
panel2.setLayout(new javax.swing.SpringLayout());
frame.add(panel2);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.setResizable(true);
frame.setVisible(true);
panel.setLocation(50, 50);
panel2.setLocation(150, 150);
}
}
The generic node/panel class that is added to the JFrame:
public class NodePanel extends JPanel {
private int x = 0, int y = 0;
private PortPanel inPortPanel;
private PortPanel outPortPanel;
int iH;
int oH;
private int width = 120;
private int height = 30;
Graphics2D g2;
public NodePanel(int input, int output) {
inPortPanel = new PortPanel(input);
this.add(inPortPanel);
outPortPanel = new PortPanel(output);
this.add(outPortPanel);
setPreferredSize(calculateDimension());
}
public void paintComponent(Graphics g) {
g2 = (Graphics2D) g;
g2.setColor(Color.black);
g2.setStroke(new BasicStroke(4));
inPortPanel.setLocation(x, y + (height - iH) / 2);
outPortPanel.setLocation(x + width, y + (height - oH) / 2);
drawNode();
}
private void drawNode() {
g2.drawRoundRect(x, y, width, height, 5, 5);
}
private Dimension calculateDimension() {
iH = inPortPanel.getPreferredSize().height;
int iW = inPortPanel.getPreferredSize().width;
oH = outPortPanel.getPreferredSize().height;
height = iH > oH ? iH + iW : oH + iW;
return new Dimension(width, height);
}
}
The port panel that is part of the Node Panel
public class PortPanel extends JPanel {
int x = 0;
int y = 0;
int portWidth = 14;
int portHeight = 14;
int count = 0;
int offset = 22;
Graphics2D g2;
public PortPanel(int i) {
this.count = i;
setPreferredSize(calculateDimensions());
}
public void paintComponent(Graphics g) {
g2 = (Graphics2D) g;
g2.setColor(Color.black);
drawPorts();
}
private void drawPorts() {
for (int i = 0; i < count; i++) {
g2.fillOval(x, y + (i * offset), portWidth, portHeight);
}
}
private Dimension calculateDimensions(){
int overallHeight = (offset * (count-1)) + portHeight;
return new Dimension(portWidth,overallHeight);
}
}
Node should be a logical class, not a GUI component class.
You should have a single drawing JPanel that can draw all the visual representation of your logical entities.
This way the model can hold multiple logical entities that all can be drawn by the single drawing JPanel without having to worry as much about layout managers.
Don't forget to call your JPanel's super.paintComponent method within its override method so your JPanel can do house-keeping painting.
Avoid giving your JPanels Graphics or Graphics2D fields as that increases the risk of your code throwing a NPE. Use the Graphics object given to your paintComponent method, and if you need to use it in another method that is called by paintComponent, pass it into that method.
I'm trying to develop a Tic Tac Toe game in Java using Graphics.
My problem is: I don't want to add any other methods inside my Grid.class (which draws the lines 3x3), but i want to draw my X or O from a class called Game. My grid class looks like the following:
import java.awt.Graphics;
import javax.swing.JPanel;
public class Grid extends JPanel{
private final int ITEM_WIDTH = 30;
private final int ITEM_HEIGHT = 30;
private final int OUTER_WIDTH = 90;
private final int OUTER_HEIGHT = 90;
public void paintComponent(Graphics g){
super.paintComponent(g);
drawOuter(g);
drawGrid(g);
}
public void drawOuter(Graphics g){
g.drawRect(0, 0, OUTER_WIDTH, OUTER_HEIGHT);
}
public void drawGrid(Graphics g){
//Vertikális
for(int i = ITEM_WIDTH; i < OUTER_WIDTH; i += ITEM_WIDTH){
g.drawLine(i, 0, i, OUTER_HEIGHT);
}
//Horizontális
for(int i = ITEM_HEIGHT; i < OUTER_HEIGHT; i += ITEM_HEIGHT){
g.drawLine(0, i, OUTER_WIDTH, i);
}
}
Thank you for your helps
My suggestion would be try to keep things as simple as possible.
Create ImageIcons that hold your X and O images
Have a GridLayout of JLabels placed in your JPanel above.
Swapping the JLabel's ImageIcon when I wanted to display an X, an O, or a blank.
Proof of concept code. Clicking on the JLabels several times will show the icons. The code has no Tic-Tac-Toe logic though:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class XorOorBlank extends JPanel {
private static final int IMG_WIDTH = 100;
private static final Color BACKGROUND = Color.LIGHT_GRAY;
private static final Color X_COLOR = Color.red;
private static final Color O_COLOR = Color.blue;
private static final Stroke X_STROKE = new BasicStroke(8f);
private static final int GAP = 8;
private static final int SIDE = 3;
private Icon blankIcon = createBlankIcon();
private Icon xIcon = createXIcon();
private Icon oIcon = createOIcon();
public XorOorBlank() {
setBackground(BACKGROUND);
setLayout(new GridLayout(SIDE, SIDE, GAP, GAP));
MouseListener mouseListener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
JLabel label = (JLabel) e.getSource();
Icon icon = label.getIcon();
if (icon == blankIcon) {
icon = xIcon;
} else if (icon == xIcon) {
icon = oIcon;
} else if (icon == oIcon) {
icon = blankIcon;
}
label.setIcon(icon);
}
};
for (int i = 0; i < SIDE; i++) {
for (int j = 0; j < SIDE; j++) {
JLabel label = new JLabel(blankIcon);
label.addMouseListener(mouseListener);
add(label);
}
}
}
private Icon createBlankIcon() {
BufferedImage img = new BufferedImage(IMG_WIDTH, IMG_WIDTH,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.white);
g2.fillRect(0, 0, IMG_WIDTH, IMG_WIDTH);
g2.dispose();
ImageIcon icon = new ImageIcon(img);
return icon;
}
private Icon createXIcon() {
BufferedImage img = new BufferedImage(IMG_WIDTH, IMG_WIDTH,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.white);
g2.fillRect(0, 0, IMG_WIDTH, IMG_WIDTH);
g2.setColor(X_COLOR);
g2.setStroke(X_STROKE);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int x1 = GAP;
int y1 = x1;
int x2 = IMG_WIDTH - GAP;
int y2 = x2;
g2.drawLine(x1, y1, x2, y2);
g2.drawLine(x2, y1, x1, y2);
g2.dispose();
ImageIcon icon = new ImageIcon(img);
return icon;
}
private Icon createOIcon() {
BufferedImage img = new BufferedImage(IMG_WIDTH, IMG_WIDTH,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.white);
g2.fillRect(0, 0, IMG_WIDTH, IMG_WIDTH);
g2.setColor(O_COLOR);
g2.setStroke(X_STROKE);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int x1 = GAP;
int y1 = x1;
int x2 = IMG_WIDTH - 2 * GAP;
int y2 = x2;
g2.drawOval(x1, y1, x2, y2);
g2.dispose();
ImageIcon icon = new ImageIcon(img);
return icon;
}
private static void createAndShowGui() {
JFrame frame = new JFrame("X or O");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new XorOorBlank());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Note: not a paintComponent method to be found.
This displays:
You have a few choices...
You Could
Extend from Grid, overriding it's paintComponent method and draw the X/Os within it (calling super.paintComponent)
You Could
Create a interface which provide a simple "draw" method, which when implemented, would paint either X/O. You could then simply add an new instance of this to the Grid class and have it paint the X/O via some kind of loop within the paintComponent method
You Could
Use, something like GridBagLayout or even OverlayLayout and place another panel over the top of the Grid class, or, using BorderLayout, add the panel directly to the Grid class, which would then be responsible for painting the X/O
You Could
Do what Hovercraft Full Of Eels has suggested...
I'm making an rpg with a custom pixel engine, and when I try to run it I just get a NullPointerException and I know why, but when I try to initialize that variable I get another one in a different location and I fix that and now it won't run at all and I still get a NullPointerException. This is the class that gets an error, and the console tells me that the error is on the line that says screen.render();
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import sprites.SpriteSheetLoader;
public class Game extends Canvas implements Runnable{
private static final long serialVersionUID = 1L;
public static final int HEIGHT = 120;
public static final int WIDTH = 120;
public static final int SCALE = 3;
public static final String NAME = "TypicalRPG";
public SpriteSheetLoader loader;
private BufferedImage img = new BufferedImage(WIDTH * SCALE, HEIGHT * SCALE, BufferedImage.TYPE_INT_RGB);
private int[] pixels = ((DataBufferInt) img.getRaster().getDataBuffer()).getData();
private boolean running = false;
private Screen screen = new Screen(WIDTH, HEIGHT, loader);
Random random = new Random();
public void start(){
running = true;
new Thread(this).start();
}
public static void main(String args[]){
Game game = new Game();
game.setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
game.setMaximumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
game.setMinimumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
JFrame jf = new JFrame(NAME);
jf.add(game);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
jf.setResizable(true);
game.start();
}
public void run(){
while(running){
tick();
render();
try{
Thread.sleep(5);
}
catch(InterruptedException e){ e.printStackTrace(); }
}
}
public void stop(){ running = false; }
public void tick(){
screen.render(0, 0, 0, 16, 16);
}
public void render(){
BufferStrategy bs = getBufferStrategy();
if(bs == null){
createBufferStrategy(3);
requestFocus();
return;
}
for(int i = 0; i < screen.pixels.length; i++){
pixels[i] = screen.pixels[i];
}
Graphics g = bs.getDrawGraphics();
g.drawImage(img, 0, 0, getWidth(), getHeight(), null);
g.dispose();
bs.show();
}
public void init(){
BufferedImage sheet = null;
try {
sheet = ImageIO.read(Game.class.getResourceAsStream("res/tiles.png"));
}
catch (IOException e) {
e.printStackTrace();
}
loader = new SpriteSheetLoader(sheet);
screen = new Screen(WIDTH, HEIGHT, loader);
}
}
and this is the class that has render():
import sprites.SpriteSheetLoader;
public class Screen{
public int[] pixels;
private SpriteSheetLoader loader;
private int w, h;
int xoff = 0, yoff = 0;
public Screen(int w, int h, SpriteSheetLoader loader){
this.loader = loader;
this.w = w;
this.h = h;
pixels = new int[w * h];
}
public void render(int xpos, int ypos, int tile, int width, int height){
loader.grabTile(tile, width, height);
xpos -= xoff;
ypos -= yoff;
for(int y = 0; y < height; y++){
if(ypos + y < 0 || ypos + y >= h) continue;
for(int x = 0; x < width; x++){
if(xpos + x < 0 || xpos + x >= w) continue;
int col = loader.pixels[x + (y * height)];
if(col != -65281) pixels[(x + xpos) + (y + ypos)] = col;
}
}
}
}
Seems like you never initiate the loader in your first class.
You define your variables like this:
public SpriteSheetLoader loader;
private Screen screen = new Screen(WIDTH, HEIGHT, loader);
Hence, you pass a null-object to your Screen. And from there you try to call a method on that null-object:
loader.grabTile(tile, width, height);
You have only declared loader, but never initiated it.
public SpriteSheetLoader loader;