Add graphics on a Jpanel Object with Thread - java

I want to add graphics in this program. The graphics will be drawn on "drawPanel" object. I have to use thread here.
No idea for drawing in a Jpanel object with thread. What will be the good efficient way to draw graphics on that Jpanel Object?
How thread and paintComponent() going to interact.Thanks.
Code:
public class LinearSearch extends JPanel{
private final Font LABEL_FONT = new Font("courier", Font.BOLD, 30);
private JPanel mainPanel;
private JPanel centerPanel;
private JPanel inputPanel;
private JPanel drawPanel;
private JLabel lblTitle;
private Button btnBack;
public LinearSearch(JPanel mainPanel) {
this.mainPanel = mainPanel;
setBackground(Color.WHITE);
initialize_All();
}
private void initialize_All() {
lblTitle = new JLabel("\"Linear Search\"", SwingConstants.CENTER);
lblTitle.setFont(LABEL_FONT);
///Center Panel
centerPanel = new JPanel();
centerPanel.setLayout(new BorderLayout());
///Input Panel
inputPanel = new JPanel();
inputPanel.setLayout(new BoxLayout(inputPanel, BoxLayout.X_AXIS));
///Draw Panel
drawPanel = new JPanel();
drawPanel.setLayout(new FlowLayout());
/// I want to add graphics on this drawPanel Jpanel
btnBack = new Button("Back");
btnBack.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
setVisible(false);
removeAll();
mainPanel.add(new CatagoriesMenu(mainPanel));
}
});
setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
setLayout(new BorderLayout(10, 10));
add(lblTitle, BorderLayout.PAGE_START);
add(centerPanel, BorderLayout.CENTER);
add(btnBack, BorderLayout.PAGE_END);
centerPanel.add(inputPanel, BorderLayout.PAGE_START);
centerPanel.add(drawPanel, BorderLayout.CENTER);
}
}

I must need to add graphics on "drawPanel" Jpanel Object.
Then you need to extend the JPanel and override the paintComponent(...) method to do your custom painting.
Read the section from the Swing tutorial on Custom Painting for more information and working examples to get you started.

Just need to add this class object from another class in add() method.
This will work (tested):
public class LinearSearchSimulation extends JPanel implements Runnable{
private final int DELAY = 50;
private JPanel mainPanel;
int x=0, y=0;
private Thread thread;
private boolean threadFlag = false;
public LinearSearchSimulation(JPanel mainPanel){
this.mainPanel = mainPanel;
setBackground(Color.WHITE);
}
public void start() {
if (threadFlag) {
return;
}
threadFlag = true;
thread = new Thread(this);
thread.start();
}
public void stop() {
if (!threadFlag) {
return;
}
threadFlag = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(x,y, 55, 55);
g.dispose();
}
public void update() {
x += 1;
y += 1;
}
#Override
public void run() {
long beforeTime, timeDiff, sleep;
beforeTime = System.currentTimeMillis();
while (threadFlag) {
update();
repaint();
timeDiff = System.currentTimeMillis() - beforeTime;
sleep = DELAY - timeDiff;
if (sleep < 0)
sleep = 2;
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
System.out.println("interrupted");
}
beforeTime = System.currentTimeMillis();
}
}
}

Related

Repaint won't happen until the for loop has escaped, with only two frames

I'm trying to do some basic Java, and I've got my frame with a shape in it. It's using a JComponent class to draw the shape, with the animation being triggered on a button click at the top.
the component code is just this added to the jpanel
public void paintComponent(Graphics g){
Dimension dim = getSize();
g.setColor(Color.GREEN);
g.fillOval(margin, 150, 100, 100);
super.paintComponent(g);
}
The animation is just done inside a for loop, that just edits the left margin so that the circle moves to the right;
int getMarg = cc.getMargin();
for(int i = 1;i < 20;i++){
getMarg = cc.getMargin();
cc.setMargin(getMarg + 1);
reValidate();
System.out.println(i);
But it doesn't seem to move until the end of the loop, moving 20 pixels at a time. I previously had a sleep function but it seemed pointless when it wouldn't animate.
Any insight? Cheers.
The whole code for anyone interested, messy and largely just to get the styling:
class Main extends JFrame{
public JPanel panel = new JPanel();
JButton button1 = new JButton("Move Right");
CreateComps cc = new CreateComps();
Main(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initUI();
}
void initUI(){
setSize(800,800);
setBackground(Color.GRAY);
setLayout(new BoxLayout(this.getContentPane(), BoxLayout.Y_AXIS));
JPanel topBar = new JPanel();
topBar.setPreferredSize(new Dimension(800,30));
topBar.setMaximumSize(new Dimension(800,30));
topBar.setLayout(new BorderLayout());
topBar.add(button1, BorderLayout.WEST);
topBar.setBackground(Color.GRAY);
JPanel container = new JPanel();
container.setLayout(new GridBagLayout());
container.setBackground(Color.DARK_GRAY);
panel.setPreferredSize(new Dimension(600,500));
panel.setMinimumSize(new Dimension(600,500));
panel.setBackground(Color.WHITE);
panel.setLayout(new BorderLayout());
panel.add(cc, BorderLayout.CENTER);
add(topBar);
add(container);
container.add(panel);
Listener listen = new Listener();
button1.addActionListener(listen);
setVisible(true);
}
public void reValidate(){
panel.revalidate();
panel.repaint();
}
public static void main (String[] args){
Main main = new Main();
}
class Listener implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Listening..");
if(e.getSource().equals(button1)){
int getMarg = cc.getMargin();
for(int i = 1;i < 20;i++){
getMarg = cc.getMargin();
cc.setMargin(getMarg + 1);
reValidate();
System.out.println(i);
}
}
}
}
}
class CreateComps extends JComponent{
int margin = 10;
public void setMargin(int marg){
margin = marg;
}
public int getMargin(){
return margin;
}
#Override
public Dimension getPreferredSize(){
return new Dimension(new Dimension(200,200));
}
#Override
public Dimension getMaximumSize(){
return new Dimension(new Dimension(200,200));
}
#Override
public Dimension getMinimumSize(){
return new Dimension(new Dimension(200,200));
}
public void paintComponent(Graphics g){
Dimension dim = getSize();
g.setColor(Color.GREEN);
g.fillOval(margin, 150, 100, 100);
super.paintComponent(g);
}
}
Without pauses, you're stacking calls to revalidate and will see nothing else than the result of the last call.
The sleep function you previously had, probably was called on the Event Dispatch Thread, which is not good at all, since you're blocking the entire event dispatching and GUI updates.
Consider either using sleep calls on another Thread :
#Override
public void actionPerformed(final ActionEvent e) {
System.out.println("Listening..");
if (e.getSource().equals(button1)) {
new Thread() {
#Override
public void run() {
int getMarg = cc.getMargin();
for (int i = 1; i < 20; i++) {
getMarg = cc.getMargin();
cc.setMargin(getMarg + 1);
reValidate();
System.out.println(i);
try {
Thread.sleep(50);
} catch (Throwable e) {
}
}
}
}.start();
}
}
Or you may also simply use a Timer, which is great for this job.

Swapping JPanel, 2nd JPanel doesn't display

I have a JFrame and 2 JPanels. One panel is the start screen with buttons. The other panel is the game screen. When I remove the start screen and display the game screen nothing happens to the JFrame. The start screen is persistent but not usable and the game runs in the background without updating the screen. When the game completes the start screen is usable again. I've searched everywhere online for a solution. They all say removeAll, revalidate, repaint, pack, or getContentPane.removeAll. I've not been able to get any of these solutions to work. Here is the driver class:
public class BallDriver extends JFrame {
private static final long serialVersionUID = 1L;
int width = 500;
int height = 500;
Container cont;
StartScreen start;
BallGame ballGame;
public BallDriver() {
cont = getContentPane();
startScreen();
}
private void startScreen() {
setTitle("BallGame");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(new Dimension(width, height));
setPreferredSize(new Dimension(500,500));
start = new StartScreen();
JButton[] startButtons = start.getButtons();
setupButtons(startButtons);
add(start, BorderLayout.CENTER);
}
private void startGame(String difficulty) throws InterruptedException {
removeAll();
setSize(new Dimension(width, height));
setPreferredSize(new Dimension(width, height));
ballGame = new BallGame(width, height);
ballGame.setPreferredSize(new Dimension(width, height));
add(ballGame, BorderLayout.CENTER);
revalidate();
repaint();
while(!ballGame.endGame()) {
ballGame.moveBall();
ballGame.repaint();
try {
Thread.sleep(2);
} catch (InterruptedException e) { e.printStackTrace(); }
}
removeAll();
add(start, BorderLayout.CENTER);
revalidate();
repaint();
}
private void setupButtons(final JButton[] buttons) {
for (JButton button : buttons) {
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Object object = e.getSource();
try {
if (object == buttons[0])
startGame(StartScreen.MODE_EASY);
if (object == buttons[1])
startGame(StartScreen.MODE_NORMAL);
if (object == buttons[2])
startGame(StartScreen.MODE_HARD);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
});
}
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
BallDriver ballDriver = new BallDriver();
ballDriver.setVisible(true);
}
});
}
}
EDIT:
I've updated the class to use cardlayout, but the same thing occurs.
public class BallDriver extends JFrame {
private static final long serialVersionUID = 1L;
int width = 500;
int height = 500;
JPanel cardPanel = new JPanel();
Container cont;
StartScreen start;
BallGame ballGame;
CardLayout cardLayout = new CardLayout();
public BallDriver() {
setTitle("BallGame");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(new Dimension(width, height));
setPreferredSize(new Dimension(width, height));
cardPanel.setLayout(cardLayout);
cont = getContentPane();
add(cardPanel, BorderLayout.CENTER);
startScreen();
}
private void startScreen() {
start = new StartScreen();
JButton[] startButtons = start.getButtons();
setupButtons(startButtons);
cardPanel.add(start, "start");
cardLayout.show(cardPanel, "start");
}
private void startGame(String difficulty) throws InterruptedException {
ballGame = new BallGame(width, height);
cardPanel.add(ballGame, "game");
cardLayout.show(cardPanel, "game");
cardPanel.revalidate();
cardPanel.repaint();
while(!ballGame.endGame()) {
System.out.println("Running");
ballGame.moveBall();
ballGame.repaint();
try {
Thread.sleep(2);
} catch (InterruptedException e) { e.printStackTrace(); }
}
}
private void setupButtons(final JButton[] buttons) {
for (JButton button : buttons) {
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Object object = e.getSource();
try {
if (object == buttons[0])
startGame(StartScreen.MODE_EASY);
if (object == buttons[1])
startGame(StartScreen.MODE_NORMAL);
if (object == buttons[2])
startGame(StartScreen.MODE_HARD);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
});
}
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
BallDriver ballDriver = new BallDriver();
ballDriver.setVisible(true);
}
});
}
}
CardLayout allows you to add multiple panels to a Container, displaying only 1 panel at a time, with the ability to switch between panels. You set the parent's layout to CardLayout, add panels to your frame specifying a name as the constraints:
CardLayout layout = new CardLayout();
JFrame frame = new JFrame();
frame.setLayout(layout);
JPanel panel1 = new JPanel();
JPanel panel2 = new JPanel();
frame.add(panel1, "first");
frame.add(panel2, "second");
The first panel added is the first tk be displayed. To switch between panels, call CardLayout#show(Container, String), passing in the parent of the panels (considered the "deck") and the name of the specific panel you want (considered the "card").
layout.show(frame, "second");
A common problem people have with CardLayout is having the parent resize to the current panel's size. This can be achieved by extending upon CardLayout

Java JFrame Graphics dont show up

public class FrameTest extends JFrame {
private JPanel contentPane;
private JPanel spielPane;
private JPanel infoPane;
private JButton btnStart;
private JLabel lblRunde;
private Monster monster1;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
FrameTest frame = new FrameTest();
frame.setVisible(true);
Monster m = new Monster();
frame.repaintSpielPanel();
}
catch (Exception e) {
e.printStackTrace();
}
}
});
}
public FrameTest() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 800, 600);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(null);
setContentPane(contentPane);
spielPane = new JPanel();
spielPane.setBounds(6, 6, 566, 566);
spielPane.setLayout(null);
spielPane.setBackground(Color.yellow);
contentPane.add(spielPane);
infoPane = new JPanel();
infoPane.setBounds(578, 6, 216, 566);
infoPane.setLayout(null);
infoPane.setBackground(Color.yellow);
contentPane.add(infoPane);
btnStart = new JButton("Start");
btnStart.setBounds(44, 6, 117, 29);
infoPane.add(btnStart);
lblRunde = new JLabel("Runde");
lblRunde.setBounds(77, 47, 61, 16);
infoPane.add(lblRunde);
monster1 = new Monster();
monster1.setVisible(true);
spielPane.add(monster1);
}
private class Monser extends JLabel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(0, 0, 10, 10);
}
}
public void repaintSpielPanel() {
spielPane.repaint();
}
}
I tried to put a Rectangle into my frame, but it isn't there. I'm just starting to program, so probably I just don't know how to get it onto the screen. Pls help!
Don't paint in a JLabel which isn't opaque. Instead do so in a JPanel.
Don't use null layouts and setBounds(...). While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
Most important, you never add an instance of your drawing component, here Monser, to anything. To see that this is true, search your code for any calls to new Monser(). If a constructor isn't being called, an instance isn't going to be created.
Also note that you're creating a Monster instance, and this is a class that you've not shown us.
You're adding it to a null layout using JPanel, and so since it has no size, it won't show at all. Again, don't use null layouts.
For example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import javax.swing.*;
#SuppressWarnings("serial")
public class MyTest extends JPanel {
private static final Color BG = Color.yellow;
private static final int GAP = 8;
private JButton btnStart = new JButton("Start");
private JLabel lblRunde = new JLabel("Runde", SwingConstants.CENTER);
private MonsterPanel monsterPanel = new MonsterPanel(600, 600, BG);
public MyTest() {
JPanel buttonPanel = createButtonPanel();
buttonPanel.setBackground(BG);
setLayout(new BorderLayout(GAP, GAP));
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
add(monsterPanel, BorderLayout.CENTER);
add(buttonPanel, BorderLayout.LINE_END);
}
private JPanel createButtonPanel() {
JPanel btnPanel = new JPanel();
btnPanel.setBackground(BG);
JPanel gridPanel = new JPanel(new GridLayout(0, 1, 0, 5));
gridPanel.setOpaque(false);
gridPanel.add(btnStart);
gridPanel.add(lblRunde);
btnPanel.add(gridPanel);
return btnPanel;
}
private static void createAndShowGui() {
MyTest mainPanel = new MyTest();
JFrame frame = new JFrame("MyFrameTest");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class MonsterPanel extends JPanel {
public static final int MONST_WIDTH = 10;
public static final Color MONST_COLOR = Color.red;
private int prefW;
private int prefH;
private int monstX = 0;
private int monstY = 0;
public MonsterPanel(int prefW, int prefH, Color bkgrnd) {
this.prefW = prefW;
this.prefH = prefH;
setBackground(bkgrnd);
}
public void setMonstXY(int x, int y) {
this.monstX = x;
this.monstY = y;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(MONST_COLOR);
g.fillRect(monstX, monstY, MONST_WIDTH, MONST_WIDTH);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(prefW, prefH);
}
}

Problems with Swing CardLayout

I'm building a Java Virtual Pet game, currently the menu panel is working fine, buttons are loading and operating, but when I click my new game button which is supposed to launch into the GamePanel Panel I get a nullpointerException.
This is the main class which builds the original frame and Jpanel to switch from.
public class MainFrame extends JPanel {
public JPanel mainPanel;
public CardLayout cl;
private final GamePanel gamePanel;
private final MenuPanel menuPanel;
/**
* Constructs the main panel to be used to switch between panels.
*
*/
public MainFrame() {
// creates a new panel to add panels to.
cl = new CardLayout();
// panel to be used as a main switch.
mainPanel = new JPanel();
Dimension size = getPreferredSize();
size.width = 600;
size.height = 600;
setPreferredSize(size);
setBackground(Color.BLACK);
add(mainPanel);
gamePanel = new GamePanel();
menuPanel = new MenuPanel();
// sets layout
mainPanel.setLayout(cl);
mainPanel.add(menuPanel, "menuPanel");
mainPanel.add(gamePanel, "gamePanel");
}
public void changePanel(String name) {
cl.show(mainPanel, name);
}
/**
* Main frame used by the game.
*
* #param args
*/
public static void main(String[] args) {
MainFrame game = new MainFrame();
JFrame frame = new JFrame("Main Window");
frame.add(game);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
}
}
This is the code from my MenuPanel class which should be accessing the changePanel() method.
public class MenuPanel extends JPanel {
MainFrame mf;
public MenuPanel() {
this.mf = mf;
Dimension size = getPreferredSize();
size.width = 600;
size.height = 600;
setPreferredSize(size);
ImageIcon menuIcon = new ImageIcon("C:\\Programming\\NetBeansProjects\\PDCMain\\src\\Data\\the_menu_title2.png");
JLabel menuLbl = new JLabel();
menuLbl.setIcon(menuIcon);
JButton newGameBtn = new JButton("New Game");
JButton loadGameBtn = new JButton("Load Game");
JButton helpBtn = new JButton("Instructions");
JButton exitBtn = new JButton("Exit");
newGameBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("New Game");
mf.changePanel("gamePanel");
}
});
loadGameBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
}
});
helpBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
showHelp();
}
});
exitBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
setLayout(new GridBagLayout());
GridBagConstraints gc = new GridBagConstraints();
gc.weightx = 0;
gc.weighty = 0.1;
gc.gridx = 0;
gc.gridy = 1;
add(menuLbl, gc);
gc.gridx = 0;
gc.gridy = 2;
add(newGameBtn, gc);
gc.gridx = 0;
gc.gridy = 3;
add(loadGameBtn, gc);
gc.gridx = 0;
gc.gridy = 4;
add(helpBtn, gc);
gc.gridx = 0;
gc.gridy = 5;
add(exitBtn, gc);
}
public void showHelp(){
String[] help = new String[100];
try {
BufferedReader br = new BufferedReader(new FileReader("instruct.txt"));
String line = "";
int i = 0;
while((line = br.readLine()) != null) {
help[i] = line;
i++;
}
JOptionPane.showMessageDialog(null, help);
} catch(IOException ex) {
System.err.println("IOException Error: " + ex.getMessage());
}
}
and here is the game panel
public class GamePanel extends JPanel{
private ButtonsPanel buttonsPanel;
private GraphicsPanel graphicsPanel;
private final JPanel gamePanel;
public GamePanel(){
super();
this._initGUI();
gamePanel = new JPanel();
Dimension size = getPreferredSize();
size.width = 600;
size.height = 600;
setPreferredSize(size);
setBackground(Color.RED);
}
private void _initGUI(){
this.buttonsPanel = new ButtonsPanel();
this.graphicsPanel = new GraphicsPanel();
this.setLayout(new BorderLayout());
this.add(buttonsPanel, BorderLayout.SOUTH);
this.add(graphicsPanel, BorderLayout.CENTER);
}
public void run(){
graphicsPanel.run();
}
}
This is my first attempt at building a GUI from scratch and using cardlayout. I can't see where it would be assigning a null value as the panel is declared in the main panel used for the switching, any ideas?
Take a second to look at this piece of code...
public static class MenuPanel extends JPanel {
MainFrame mf;
public MenuPanel() {
this.mf = mf;
Basically, you are assigning (this) mf to this.mf, but since mf is already null, this.mf will remain null...
You should be passing a reference of MainFrame to your MenuPanel
Now, because I hate passing around references of UI components to other parts of the program, which should have no idea about the rest of the UI and don't direct control over them, I would advice against passing MainFrame blinding like this.
Instead, create a interface which provides the functionality you expect that MenuPanel should be allowed to perform (newGame, loadGame, showInstructions, exit) for example and allow the implementation of this interface (probably the MainFrame) to deal with it
You would then pass the instance of this interface to MenuPanel...This limits the exposure of classes to other classes which don't need those classes functionality and decouples your code so you can pass any old instance of the interface to the class without having to rewrite whole sections of your code...
Updated with a basic concept
Start with some kind game controller interface, for example...
public interface GameController {
public void newGame();
public void loadGame();
public void showInstructions();
public void exit();
}
This describes the actions that can take against the implementation of this interface.
Use MainFrame and implement the GameController...
public class MainFrame extends JPanel implements GameController {
//...
public void newGame() {
}
public void loadGame() {
}
public void showInstructions() {
}
public void exit() {
}
Change MenuPane to require an instance of GameController when it's constructed...
public class MenuPanel extends JPanel {
private GameController controller;
public MenuPanel(GameController gameController) {
this.controller = gameController;
//...
Now, when you handle the user interaction, you would pass the request to the controller....
newGameBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("New Game");
controller.newGame();
}
});
In this way, the MenuPanel doesn't know how a new game is created, it just knows that it will be taken care of...
Now, you will need to update the newGame method in MainFrame to do something, for example...
public void newGame() {
// Setup the new game...
cl.show(mainPanel, "gamePanel");
}
This places all the responsibility squarely with the implementation of the GameController to make decisions about how certain actions should take place, decouples the process, so if you change the implementation of the GameController to some other class, you don't need to worry about updating the MenuPanel, cause it's only interested in taking to the GameController...nice ;)

Java repainting when pushing a button

I've hit a wall (in my brain) trying to update my board on button presses. Am I right in thinking that the GameBoard class is the one that needs to be repaint()ed?
GameBoard.java
public class GameBoard extends Panel {
static Compass compass = new Compass();
private static final long serialVersionUID = 1;
Graphics2D g2d;
static final Dimension WINDOW_SIZE = new Dimension(1150, 800);
public void boardMaker() throws Exception {
JFrame frame = new JFrame("Display image");
JPanel panel = new JPanel();
/* unimportant stuff
.....
*/
//
DieRoll roll = new DieRoll("Roll Dies");
roll.setC(compass);
roll.setG2D(g2d);
//
Button button = new Button("new");
button.setGameBoard(this);
JPanel buttonPanel = new JPanel();
buttonPanel.add(button);
buttonPanel.add(roll);
buttonPanel.setPreferredSize(new Dimension(200,100));
frame.getContentPane().add(buttonPanel, BorderLayout.NORTH);
//
frame.getContentPane().add(panel);
frame.setVisible(true);
}
public void paint(Graphics g) {
// not important I think
}
}
Button.java
public class Button extends JButton implements ActionListener {
private static final long serialVersionUID = 1L;
JPanel panel = new JPanel();
JFrame frame = new JFrame();
Compass c = new Compass();
GameBoard gb = new GameBoard();
Button(String text) {
this.setText(text);
this.addActionListener(this);
}
void setGameBoard(GameBoard gb) {
this.gb = gb;
}
#Override
public void actionPerformed(ActionEvent e) {
gb.g2d.setColor(Color.black);
gb.g2d.fillRect(100, 100, 100, 200);
gb.repaint();
}
}
This gives a null pointer exception. So any idea how to repaint my GameBoard? I'm not mad if I've to rewrite everything because of stupidity! ;)
Thanks
You have the wrong idea about how to draw in Java. Components like Panels draw themselves, and all drawing takes place on the UI thread.
Check out this tutorial: docs.oracle.com/javase/tutorial/2d/index.html
The article Painting in AWT and Swing may offer some perspective on application-triggered painting. The example below illustrates the principle. Note that setForeground() calls repaint() automatically because the foreground color is a bound property, but you can always call it yourself.
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;
public class SwingPaint {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
final GamePanel gp = new GamePanel();
f.add(gp);
f.add(new JButton(new AbstractAction("Update") {
#Override
public void actionPerformed(ActionEvent e) {
gp.update();
}
}), BorderLayout.SOUTH);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
private static class GamePanel extends JPanel {
private static final Random r = new Random();
public GamePanel() {
this.setForeground(new Color(r.nextInt()));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(320, 240);
}
public void update() {
this.setForeground(new Color(r.nextInt()));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension size = this.getSize();
int d = Math.min(size.width, size.height) - 10;
int x = (size.width - d) / 2;
int y = (size.height - d) / 2;
g.fillOval(x, y, d, d);
g.setColor(Color.blue);
g.drawOval(x, y, d, d);
}
}
}

Categories

Resources