What can cause a Swing application to inconsistently throw null pointer exceptions? - java

I have a two class project, one class reads one file, and checks each entry in said file against a website, and posts the return data in another file.
If the return data says true(for example), the data point in the file is flashed on the screen. This functionality works.
I invoke this through the following if statement within a while loop.
if (!query.text().contains("unavailable") && !query.text().contains("at least 3 characters long to acquire.") && line != null) {
HitBox h = new HitBox(line); //GUI Class.
fos.write(query.text().getBytes());
fos.write("\n".getBytes());
fos.flush();
}
Below is my GUI class.
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
public class HitBox {
private Timer t;
JWindow frame = new JWindow();
public HitBox(String s) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager
.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
frame.setAlwaysOnTop(true);
t = new Timer(1000 * 5, new ActionListener() {
public void actionPerformed(ActionEvent e2) {
SwingUtilities.getWindowAncestor(frame.getComponent(0))
.dispose();
}
});
}
});
frame.setBackground(new Color(0, 0, 0, 0));
TranslucentPane tp = new TranslucentPane(s);
frame.setContentPane(tp);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
t.start();
}
}
class TranslucentPane extends JPanel {
public TranslucentPane(String s) {
add(new JLabel(s));
setOpaque(false);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(0.85f));
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
}
}
This happens using the same set of input data, even if I override the web query, and just return a set value, # a random point in runtime, a JWindow will appear, a nullpointer will be thrown (# my call of the start method of my timer object).
This leads me to believe I'm implementing the timer incorrectly; I'm intrigued by how with consistent data, and return, there is variation in the point it throws the nullpointer.
Exception in thread "main" java.lang.NullPointerException
at HitBox.<init>(HitBox.java:51)
at OriginalGangster.main(OriginalGangster.java:38)

You're not starting your GUI on the GUI thread. You need to move it into the Runnable and be sure to start the Timer after it has been constructed.
e.g.,
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.Timer;
import javax.swing.UIManager;
public class TestHitBox {
public static void main(String[] args) {
String text = "Hello world! This is Hovercraft!";
int seconds = 5;
float composite = 0.85f;
float points = 48f;
HitBox.showMessage(text, seconds, composite, points);
}
}
class HitBox {
public static void showMessage(final String text, final int seconds, final float composite, final float points) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager
.getSystemLookAndFeelClassName());
} catch (Exception ex) {
ex.printStackTrace();
}
final JWindow frame = new JWindow();
frame.setBackground(new Color(0, 0, 0, 0));
TranslucentPane tp = new TranslucentPane(text, composite, points);
frame.setContentPane(tp);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setAlwaysOnTop(true);
new Timer(1000 * seconds, new TimerListener(frame)).start();
}
});
}
}
class TimerListener implements ActionListener {
private JWindow frame;
public TimerListener(JWindow frame) {
this.frame = frame;
}
#Override
public void actionPerformed(ActionEvent e) {
frame.dispose();
((Timer) e.getSource()).stop();
}
}
#SuppressWarnings("serial")
class TranslucentPane extends JPanel {
private float composite;
public TranslucentPane(String s, float composite, float points) {
this.composite = composite;
JLabel label = new JLabel(s);
label.setFont(label.getFont().deriveFont(Font.BOLD, points));
add(label);
setOpaque(false); // this breaks a rule
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(composite));
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.dispose(); // dispose of any graphics we create
}
}
Code updated.
The main code has been moved to a public static method, since this appears to be code to display a message to the user and not to share state with any other code, similar to a JOptionPane message, so I'm making it behave like a JOptionPane.
Timer's ActionListener moved out of constructor for cleanliness.
Added parameters for alpha composite, display time, and message font point size.

Related

Java -Repaint() not working when i want to call it

I wonder why the repiant() method is not working as intended. My Code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;
public class RightPanel extends JPanel implements ActionListener{
JButton buttono;
JButton buttonu;
MyFrame frame;
ButtomPanel s;
public RightPanel(MyFrame frame){
super();
this.frame=frame;
s= new ButtomPanel(frame);
this.setPreferredSize(new Dimension((frame.getWidth()/3),frame.getHeight()));
setBackground(Color.green);
setLayout(new BorderLayout());
buttono = new JButton ("up");
buttonu = new JButton ("down");
buttono .addActionListener(this);
buttonu .addActionListener(this);
add(buttono , BorderLayout.NORTH);
add(buttonu , BorderLayout.SOUTH);
setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource()==buttono) {
System.out.println("Up");
s.x=s.x+10;
s.repaint();
}
}
I want to Repaint following class:
import java.awt.Color;
import java.util.Timer;
import java.util.TimerTask;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class ButtomPanel extends JPanel {
MyFrame frame;
BufferedImage image;
boolean geklickt=false;
public static int x=0;;
public ButtomPanel(MyFrame frame) {
super();
this.frame=frame;
this.setPreferredSize(new Dimension(((frame.getWidth()/3)*2),585));
setBackground(Color.blue);
java.net.URL resource = getClass().getResource("/resources/siegel.jpg");
try {
image = ImageIO.read(resource);
} catch (IOException e) {
e.printStackTrace();
}
setVisible(true);
}
public void paint(Graphics g) {
super.paint(g);
g.drawImage(image, 150+x, 150+x, 150, 150, null);}
}
}
I want to change with the ActionListener in the RightPanel Class the x from the BottomPanel Class and then repaint it, but it doesnt work. I can raise the x, but s.repaint() does not call the paint-Method.
s is never added to anything, so it will never be painted.
this.setPreferredSize(new Dimension((frame.getWidth()/3),frame.getHeight())); is a horribly bad idea. The window should be conforming to the size of the components, not the other way round. In fact, there's never really a good reason to expose MyFrame to the components this way, it provides these components control over MyFrame which they should never have.
java.net.URL resource = getClass().getResource("/resources/siegel.jpg");
try {
image = ImageIO.read(resource);
} catch (IOException e) {
e.printStackTrace();
}
is a bad idea. The class should be throwing the exception to those using it, so that they know something has gone wrong. Doing it this way not only consumes the error in a way which is difficult to trace, but also sets you up for a NullPointerException when you call g.drawImage(image, 150+x, 150+x, 150, 150, null)
Don't override paint
public void paint(Graphics g) {
super.paint(g);
g.drawImage(image, 150+x, 150+x, 150, 150, null);
}
prefer paintComponent instead. paint is actually a very complex method. See Painting in AWT and Swing and Performing Custom Painting for more details
public static int x=0; is a bad idea for a three reasons:
static is not your friend. What happens when you have more than one instance of ButtomPanel? Generally speaking, when you use static in this way, it's a red flag telling you that your design is wrong.
public is providing uncontrolled access to the property. This is generally discouraged and you could be preferring to use setters and getters to interact with the property.
JPanel already has a x property, which could make it confusing (ie if if someone used getX instead of x). Better to rename it to something like imageX of imageXOffset.
Runnable example...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new RightPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class RightPanel extends JPanel implements ActionListener {
JButton buttono;
JButton buttonu;
ButtomPanel s;
public RightPanel() throws IOException {
super();
s = new ButtomPanel();
setBackground(Color.green);
setLayout(new BorderLayout());
buttono = new JButton("up");
buttonu = new JButton("down");
buttono.addActionListener(this);
buttonu.addActionListener(this);
add(buttono, BorderLayout.NORTH);
add(buttonu, BorderLayout.SOUTH);
add(s);
setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == buttono) {
System.out.println("Up");
s.addToImageXOffset(10);
}
}
}
public class ButtomPanel extends JPanel {
private BufferedImage image;
private int imageXOffset = 0;
public ButtomPanel() throws IOException {
super();
setBackground(Color.blue);
image = ImageIO.read(getClass().getResource("/images/Heart.png"));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public void addToImageXOffset(int delta) {
setImageXOffset(getImageXOffset() + delta);
}
public void setImageXOffset(int imageXOffset) {
this.imageXOffset = imageXOffset;
repaint();
}
public int getImageXOffset() {
return imageXOffset;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
g.drawImage(image, 150 + imageXOffset, 150 + imageXOffset, this);
}
}
}

make Jlabel live in paintComponent

I have quite a similar problem to answered before in keyword in title, but I have the situation not satisfied me. In this part of code I wont to show current position of "craft". I can see it in console (function craft.Print()), but also want to have it in label. The label exist, but or is not "live" or in every loop is copied and fills the screen. I commented the way to make it visible only one time, but it is not pretty way to do it I think.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import javax.swing.border.Border;
public class Board extends JPanel implements ActionListener {
int i=0;
Craft craft = new Craft();//this is object to be moved
private static final long serialVersionUID = 1L;
private Timer timer;
private final int DELAY = 100;
public Board() {
initBoard();
}
private void initBoard() {
addKeyListener(new TAdapter());
setFocusable(true);
setBackground(Color.BLACK);
timer = new Timer(DELAY, this);
timer.start();
}
#Override
public void paintComponent(Graphics g) {
// if(craft.getX()==30&&craft.getY()==30&&i==0){
JLabel label = new JLabel("It is, but I wont this to be live:"+craft.getX());//craft.getX() current position of the craft
label.setHorizontalAlignment(SwingConstants.CENTER);
label.setVerticalAlignment(SwingConstants.TOP);
label.setBackground(Color.GRAY);
label.setForeground(Color.WHITE);
Border border = BorderFactory.createLineBorder(Color.blue);
label.setBorder(border);
revalidate();
add(label);
repaint();
i=1;
// }
craft.Print();//to watch position in console
super.paintComponent(g);
doDrawing(g);
Toolkit.getDefaultToolkit().sync();
}
private void doDrawing(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(craft.getImage(), craft.getX(), craft.getY(), this);
g2d.setColor(Color.RED);
g2d.drawRect(craft.HallEntry[0],craft.HallEntry[1],
craft.PPDX,craft.PPDY);
g2d.setColor(Color.GREEN);
g2d.drawRect(craft.BathroomEntry[0], craft.BathroomEntry[1],
craft.DLX,craft.DLY);
g2d.setColor(Color.BLUE);
g2d.drawRect(craft.LivingRoomEntry[0],craft.LivingRoomEntry[1],
craft.DSX,craft.DSY);
g2d.setColor(Color.PINK);
g2d.drawRect(craft.KitchenEntry[0],craft.KitchenEntry[1],
craft.DKX,craft.DKY);
}
#Override
public void actionPerformed(ActionEvent e) {
craft.move();
repaint();
}
public class TAdapter extends KeyAdapter {
#Override
public void keyReleased(KeyEvent e) {
craft.keyReleased(e);
}
#Override
public void keyPressed(KeyEvent e) {
craft.keyPressed(e);
}
}

Java is drawing 2 boxes

So I am making a little game to learn some graphical java and I am having trouble with a button. It is drawing 2, one is the correct size and in the correct location and then there is a very small button centered at the top of the application. THere should only be the one button at (0,0,200,50). I do not know what is wrong but here is the code for the button, if you need something more then this let me know!
ImageIcon test = new ImageIcon("nhButton.png");
JButton jb = new JButton(test);
jb.setBounds(0, 0, 200, 50);
jb.setVisible(true);
add(jb);
EDIT1: the 2 classes where error will be: board.java:
import javax.swing.JPanel;
import javax.swing.JButton;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class Board extends JPanel {
public Board() {
}
#Override
public void paintComponent(Graphics g) {
ImageIcon test = new ImageIcon("nhButton.png");
JButton jb = new JButton(test);
jb.setBounds(0, 0, 200, 50);
jb.setVisible(true);
add(jb);
}
private void drawRectangle(Graphics g, int x, int y, int width, int height) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawRect(x, y, width, height);
}
}
and the main:
import java.awt.EventQueue;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import javax.swing.JFrame;
public class main extends JFrame {
public main() {
initUI();
}
private void initUI() {
add(new Board());
setSize(800, 600);
setTitle("Application");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
main ex = new main();
ex.setVisible(true);
}
});
}
}
If you try to resize window, you will see that buttons are spawning.
This happens because of your paintComponent method, which is called every painting iteration.
You should move button addition, for example, to constructor which is called once:
public Board() {
ImageIcon test = new ImageIcon("nhButton.png");
JButton jb = new JButton(test);
jb.setBounds(0, 0, 200, 50);
jb.setVisible(true);
add(jb);
}

Java Swing: Transparent PNG permanently captures original background

I have the following code:
import javax.swing.JWindow;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
public class sutff extends JWindow
{
//Get transparent image that will be use as splash screen image.
Image bi=Toolkit.getDefaultToolkit().getImage("window.png");
ImageIcon ii=new ImageIcon(bi);
public sutff()
{
try
{
setSize(ii.getIconWidth(),ii.getIconHeight());
setLocationRelativeTo(null);
show();
//Thread.sleep(10000);
//dispose();
//JOptionPane.showMessageDialog(null,"This program will exit !!!","<>",JOptionPane.INFORMATION_MESSAGE);
}
catch(Exception exception)
{
exception.printStackTrace();
}
}
//Paint transparent image onto JWindow
public void paint(Graphics g)
{
g.drawImage(bi,0,0,this);
}
public static void main(String[]args)
{
sutff tss=new sutff();
}
}
The purpose is to create a window that is translucent and resembles Windows Aero-style glass. I have the following transparent png that I am using:
http://i.imgur.com/5UNGbsr.png
The problem is that since its transparent, its suppose to show the things behind the window, right? That's what it does when first executed, except whatever window is behind this "transparent window" when it first starts up, the program somehow creates an "image" of that and permanently attaches it with the window. So even if I minimize the windows behind this "transparent window," the image of the first background window remains.
Here is a screenshot:
When I took this screen shot, I had already minimized the command prompt and the IDE which can be seen in the background, yet it still remains in the background of the window.
What am I doing wrong?
Don't override the paint() method of a top level container, especially when you don't invoke super.paint(). This will cause painting problems. If you ever do need to do custom painting then you should override the paintComponent() method of JPanel (or JComponent) and then add the panel to the window/frame. Read the Swing tutorial on Custom Painting. This advice is given daily, I don't know why people still try to override paint()???
However this is only one of your problems. The better solution is to add your image to a JLabel and then add the label to the window. You will also need to make the window background transparent:
import javax.swing.*;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import java.awt.*;
import java.awt.Image;
import java.awt.Toolkit;
public class Stuff extends JWindow
{
//Get transparent image that will be use as splash screen image.
Image bi=Toolkit.getDefaultToolkit().getImage("transparent.png");
ImageIcon ii=new ImageIcon(bi);
public Stuff()
{
try
{
setBackground( new Color(0, 0, 0, 0) );
setSize(ii.getIconWidth(),ii.getIconHeight());
setLocationRelativeTo(null);
JLabel label = new JLabel(ii);
add(label);
show();
//Thread.sleep(10000);
//dispose();
//JOptionPane.showMessageDialog(null,"This program will exit !!!","<>",JOptionPane.INFORMATION_MESSAGE);
}
catch(Exception exception)
{
exception.printStackTrace();
}
}
/*
//Paint transparent image onto JWindow
public void paint(Graphics g)
{
super.paint(g);
g.drawImage(bi,0,0,this);
}
*/
public static void main(String[]args)
{
Stuff tss=new Stuff();
}
}
The problem is, you window is actually transparent. Java still thinks that the Window opaque and therefore won't update the graphics in such away as to show what's actually behind.
Creating a transparent window is relatively simple in Java since Java 1.6.10 (I think)
The following is a very simple example, using a semi transparent paint effect that will allow what ever falls below the window to continue to be painted correctly.
import com.sun.awt.AWTUtilities;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TransaprentBlur {
public static void main(String[] args) {
new TransaprentBlur();
}
public TransaprentBlur() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setUndecorated(true);
frame.setBackground(new Color(0, 0, 0, 0));
// Java 6...
// AWTUtilities.setWindowOpaque(frame, true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setOpaque(false);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
System.exit(0);
}
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Shape shape = new RoundRectangle2D.Float(0, 0, getWidth() - 1, getHeight() - 1, 20, 20);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(new Color(225, 225, 225, 128));
g2d.fill(shape);
g2d.setColor(Color.GRAY);
g2d.draw(shape);
g2d.dispose();
}
}
}
Update with image example
Screen shoots showing windows been moved behind the window...
Basically, all you need to do, is place you image rendering code with in the paintComponent method of TestPane
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TransaprentBlur {
public static void main(String[] args) {
new TransaprentBlur();
}
public TransaprentBlur() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setUndecorated(true);
frame.setBackground(new Color(0, 0, 0, 0));
// Java 6...
// AWTUtilities.setWindowOpaque(frame, true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private BufferedImage image;
public TestPane() {
try {
image = ImageIO.read(getClass().getResource("/5UNGbsr.png"));
} catch (IOException ex) {
}
setOpaque(false);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
System.exit(0);
}
}
});
}
#Override
public Dimension getPreferredSize() {
return image == null ? super.getPreferredSize() : new Dimension(image.getWidth(), image.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - image.getWidth()) / 2;
int y = (getHeight() - image.getHeight()) / 2;
g2d.drawImage(image, x, y, this);
g2d.dispose();
}
}
}
}

paintComponent draws other components on top of my drawing

I'm trying to build a simple paint tool. The mouseDrag events creates a new ellipse and causes my JPanel to repaint().
This works fine so far.
However, if I press any button (or any other UI component) before firing the mouseDrag event for the first time, the button is painted in the upper left corner of my panel.
I have isolated the code into this test application:
import java.awt.BasicStroke;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test extends JFrame
{
public Test()
{
final JPanel paintPanel = new JPanel(){
#Override
protected void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
g2d.setPaintMode();
g2d.setStroke(new BasicStroke(1));
g2d.fillRect(100, 100, 10, 10);
}
};
paintPanel.setPreferredSize(new Dimension(300,300));
paintPanel.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e)
{
paintPanel.repaint();
}
});
this.setLayout(new FlowLayout());
this.add(paintPanel);
this.add(new JButton("Dummy"));
this.pack();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
public static void main(String... args)
{
new Test();
}
}
A Screenshot for "seeing" the problem in my Main application
+1 to #MadProgrammer's answers.
You should have super.paintComponent(..) as the first call in your overriden paintComponent()
Do not extend JFrame unnecessarily
Create and minipulate Swing components via EDT
Dont call setPrefferedSize() rather override getPrefferedSize()
Here is an example which incorporates my advice's and #MadProgrammer's:
import java.awt.BasicStroke;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
JFrame frame;
public Test() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final PaintPanel paintPanel = new PaintPanel();
paintPanel.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
paintPanel.addRect(e.getX(), e.getY());
}
});
frame.setLayout(new FlowLayout());
frame.add(paintPanel);
frame.add(new JButton("Dummy"));
frame.pack();
frame.setVisible(true);
}
public static void main(String... args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test();
}
});
}
}
class PaintPanel extends JPanel {
public PaintPanel() {
addRect(100, 100);
}
ArrayList<Rectangle> rects = new ArrayList<>();
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setPaintMode();
for (Rectangle r : rects) {
g2d.setStroke(new BasicStroke(1));
g2d.fillRect(r.x, r.y, r.width, r.height);
}
}
public void addRect(int x, int y) {
rects.add(new Rectangle(x, y, 10, 10));
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
}
You're not calling super.paintComponent.
The graphics context used for a paint cycle is shared between all the components begin painted, this means if you don't take care to clear it before painting onto, you will end up with what ever was painted before you.
One of the jobs of paintComponent is to prepare the graphics for painting

Categories