Java Graphics repaint behavior - java

I have been searching for a reason for this behavior in my code for quite some time now. I do not want to dive too deeply into the Swing API to figure why this occurs. I would appreciate any information regarding what causes this problem.
This is a simplified version of the application I am writing, the problems seems to be when I click draw the first time, the image will not paint onto the panel, but when I click it the second time, it would paint the image perfectly. Any drawing done after will work correctly, but the initial paint problem is annoying me greatly. Thanks for any help! :)
public class ImageTester {
public static void main(String[] args) {
final JFrame window = new JFrame("Image Tester");
final JPanel pane = new JPanel();
JButton draw = new JButton("Draw");
JButton paint = new JButton("Paint");
Toolkit k = Toolkit.getDefaultToolkit();
final Image i = k.createImage("tester.jpg");
//pane.getGraphics().drawImage(i, 22, 15, null);
draw.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
System.out.println(pane.getGraphics());
pane.getGraphics().drawRect(5, 5, 15, 15);
pane.getGraphics().drawImage(i, 15, 15, null);
System.out.println("Performance");
}
});
paint.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
}
});
pane.add(draw);
pane.add(paint);
window.add(pane);
window.setVisible(true);
window.setSize(new Dimension(400, 400));
window.setLocationRelativeTo(null);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

Besides the advice of camickr..
Images load asynchronously using Toolkit.createImage(). Either use ImageIO.read(URL/File/InputStream) or add a MediaTracker.
E.G.
On first run I see.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.URL;
import javax.imageio.ImageIO;
public class ImageTester {
public static void main(String[] args) throws Exception {
final JFrame window = new JFrame("Image Tester");
JButton draw = new JButton("Draw");
JButton paint = new JButton("Paint");
final Image i = ImageIO.read(new URL(
"http://pscode.org/media/citymorn2.jpg"));
ImagePanel gui = new ImagePanel();
gui.setImage(i);
draw.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
}
});
paint.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
}
});
gui.add(draw);
gui.add(paint);
window.add(gui);
window.setVisible(true);
window.setSize(new Dimension(400, 400));
window.setLocationRelativeTo(null);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
class ImagePanel extends JPanel {
Image i;
public void setImage(Image image) {
i = image;
}
public void paintComponent(Graphics g) {
//System.out.println(pane.getGraphics());
super.paintComponent(g);
g.drawRect(5, 5, 15, 15);
g.drawImage(i, 15, 15, null);
System.out.println("Performance");
}
}

Don't use the getGraphics() method. That painting will be lost the next time Swing determines a component needs to repaint itself.
Custom painting is done by overriding the paintComponent() method of a JPanel (or JComponent) and then you add the panel to the frame.
See Custom Painting for more information and examples.

When you use createImage(), the image data is loaded, but it isn't translated into renderable pixels until it knows what component it's going to be drawn on. The Toolkit.prepareImage() method can do this. Add this line to the end of your program, and the paint problem will go away:
k.prepareImage(i, -1, -1, pane);

Related

Why doesn't the image paint over my JPanel?

I have been struggling with this for some time. At first, I only used ActionListener, then I added the paintComponent, but I have no idea what to put there. I read some tutorials and used their code as an example, but it still doesn't work. Right now, the end result is the same as it was without PaintComponent.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Scream extends JPanel {
private JButton button = new JButton("OK");
private Color screenColor;
private JPanel panel = new JPanel();
private JFrame frame;
private Dimension screenSize;
private ImageIcon image;
private JLabel label = new JLabel(image);
private int x;
private int y;
private boolean mouseClicked;
public Scream() {
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e ) {
if (e.getSource() == button) {
mouseClicked = true;
frame.getContentPane().add(label);
frame.setSize(image.getIconWidth(), image.getIconHeight());
panel.repaint();
}
}
});
frame = new JFrame ("Existential angst");
screenColor = new Color(150, 100, 0);
panel.setBackground( screenColor );
frame.add(button, BorderLayout.PAGE_END);
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1300, 700);
frame.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
image.paintComponent(this, g, 1300, 700);
}
public static void main (String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Scream scream = new Scream();
}
});
}
}
If you are trying to dynamically add an image to a panel then you need to add the label to the panel. There is no need for any custom painting.
The basic code for adding components to a visible GUI is:
panel.add(...);
panel.revalidate();
panel.repaint();
Also, don't attempt to set the size of the frame to the size of the image. A frame contains a titlebar and borders. Instead you can use frame.pack();
I noticed a couple of issues:
image is never initialized to anything so it is null, effectively making the label empty. I assume maybe your example was just incomplete?
Once I initialized the image to something, your example still did not work. Turns out adding label without specifying any constraint basically does nothing (I assume since adding a component to a border layout without a constraint puts it in the center where panel already is). When I added the label to BorderLayout.NORTH, everything worked (though resizing the frame to the size of the image makes it only partially visible since the frame includes the OK button)

Detecting mouse movement on screen

I created a MouseMotionDetection class which role is just to detect that the user has moved the mouse anywhere on screen.
For this purpose I created a new JFrame inside my class constructor with the screen size which is invisible, so basically I am observing mouse motion all over the screen.
But, I have a weird bug:
In the code's current form, once this class is activated I only detect ONE mouse motion and nothing else, it stops working right after that. But, if I put the line which sets the frame backfround to 0f,0f,0f,0f (transparent) in comments and then activate, the whole screen becomes grey and I keep tracking all the mouse motions just as I desired (I just can't see anything).
I really do not understand why this happens, haven't seen related issues around, nor in this related javadoc, which discusses MouseMotion events.
This is the code:
public class MouseMotionDetection extends JPanel
implements MouseMotionListener{
public MouseMotionDetection(Region tableRegion, Observer observer){
addMouseMotionListener(this);
setBackground(new Color(0f,0f,0f,0f));
JFrame frame = new JFrame();
frame.setUndecorated(true);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
frame.setSize(screenSize);
frame.setBackground(new Color(0f,0f,0f,0f));
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setAlwaysOnTop(true);
JComponent contentPane = this;
contentPane.setOpaque(true);
frame.getContentPane().add(contentPane, BorderLayout.CENTER);
frame.setVisible(true);
}
#Override
public void mouseDragged(MouseEvent arg0) {
}
#Override
public void mouseMoved(MouseEvent arg0) {
System.out.println("mouse movement detected");
}
A completely transparent frame does not receive mouse events.
Here is an alternative using the MouseInfo. This works if the components of the app. are invisible (transparent), unfocused or minimized.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class MouseMoveOnScreen {
Robot robot;
JLabel label;
GeneralPath gp;
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
MouseMoveOnScreen() throws AWTException {
robot = new Robot();
label = new JLabel();
gp = new GeneralPath();
Point p = MouseInfo.getPointerInfo().getLocation();
gp.moveTo(p.x, p.y);
drawLatestMouseMovement();
ActionListener al = new ActionListener() {
Point lastPoint;
#Override
public void actionPerformed(ActionEvent e) {
Point p = MouseInfo.getPointerInfo().getLocation();
if (!p.equals(lastPoint)) {
gp.lineTo(p.x, p.y);
drawLatestMouseMovement();
}
lastPoint = p;
}
};
Timer timer = new Timer(40, al);
timer.start();
}
public void drawLatestMouseMovement() {
BufferedImage biOrig = robot.createScreenCapture(
new Rectangle(0, 0, d.width, d.height));
BufferedImage small = new BufferedImage(
biOrig.getWidth() / 4,
biOrig.getHeight() / 4,
BufferedImage.TYPE_INT_RGB);
Graphics2D g = small.createGraphics();
g.scale(.25, .25);
g.drawImage(biOrig, 0, 0, label);
g.setStroke(new BasicStroke(8));
g.setColor(Color.RED);
g.draw(gp);
g.dispose();
label.setIcon(new ImageIcon(small));
}
public JComponent getUI() {
return label;
}
public static void main(String[] args) throws Exception {
Runnable r = new Runnable() {
#Override
public void run() {
JPanel ui = new JPanel(new BorderLayout(2, 2));
ui.setBorder(new EmptyBorder(4, 4, 4, 4));
try {
MouseMoveOnScreen mmos = new MouseMoveOnScreen();
ui.add(mmos.getUI());
} catch (AWTException ex) {
ex.printStackTrace();
}
JFrame f = new JFrame("Track Mouse On Screen");
// quick hack to end the frame and timer
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(ui);
f.pack();
f.setLocationByPlatform(true);
f.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
}
I believe that MouseEvents are not generated for transparent pixels. That is the MouseEvent is dispatched to the "visible" component under the frame.
So to receive the event you can't use absolute transparency. But you might be able to get away with using an alpha value of 1. I doubt you will notice a difference in the painting of the "transparent frame".
So I would use code like the following:
//frame.setBackground(new Color(0f,0f,0f,0f));
frame.setBackground(new Color(0f, 0f, 0f, 1f));
The following is not needed since you set the "contentPane" transparent when you add it to the frame:
//setBackground(new Color(0f,0f,0f,0f));
It should be noted that this code will only work when your application has focus.

My JRadioButton change look with setOpaque(false)

I want to remove the background of my JRadioButton but still keeping the same look & feel.
Images will speak by themselves:
When I do this code:
JRadioButton myJRadioButton = new JRadioButton("My JRadioButton");
add(myJRadioButton);
I get this:
And when I run it with this code:
JRadioButton myJRadioButton = new JRadioButton("My JRadioButton");
myJRadioButton.setForeground(Color.white); //To see it on the black background.
myJRadioButton.setOpaque(false);
add(myJRadioButton);
I get this:
I've something like a "star" instead of a great and beautiful circle.
And what I want is to keep the great and beautiful circle of the first image but without the default background according to it.
Document says
public void setOpaque(boolean isOpaque)
If true the component paints every pixel within its bounds. Otherwise,
the component may not paint some or all of its pixels, allowing the
underlying pixels to show through. The default value of this property
is false for JComponent. However, the default value for this property
on most standard JComponent subclasses (such as JButton and JTree) is
look-and-feel dependent.
It says all.
You can create a class which extends the JRadioButton and add all the properties inside the class:
setOpaque(false);
setContentAreaFilled(false);
setBorderPainted(false);
setForeground(Color.white);
setBackground(Color.BLACK);
Sample output:
Code:
public class Sample extends JPanel {
public Sample() {
super(new BorderLayout());
setBackground(Color.BLACK);
TransparentButton testButton = new TransparentButton("hello");
testButton.setSelected(true);
add(testButton, BorderLayout.LINE_START);
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("Hello Word demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new Sample();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
class TransparentButton extends JRadioButton {
public TransparentButton(String string) {
super(string);
setOpaque(false);
setContentAreaFilled(false);
setBorderPainted(false);
setForeground(Color.white);
setBackground(Color.BLACK);
}
}
}

JButton only appears on mouse over?

Here is my code: I took out some stuff that I felt wasn't necessary. I might've took out some brackets too, but I'm just trying to show the content I have.
What happens is, when I run the program, the background image paints (it's a PNG in resources), and only ONE button appears (my PLAY button), which is the first button - it's auto-selected.
I actually have four buttons but I've only included PLAY and INSTRUCTIONS in my code. The other three don't show up unless I mouse over them. I know it's probably something weird with the paint method, but I don't know how to fix it.
If I select a different button and minimize the window then open it again, that selected button is the only one that appears. I have to mouse over to get the other buttons to appear.
I've added super.paint() to the paint method too and I get all my buttons but the background is grey.
I think the problem is super.paint() paints all my buttons, and g.drawImage(bg, 0, 0, null) only paints my background and I can't do one without painting over the other.
Sorry if this was a mess. I'm new at Java and I have trouble articulating what I'm trying to say.
public class MainMenu extends JFrame {
private JPanel contentPane;
/**
* Launch the application.
*/
//variables
public static Image bg;
public static void main(String[] args) {
MainMenu mainFrame = new MainMenu();
mainFrame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
mainFrame.setResizable(false);
mainFrame.setLocationRelativeTo(null);
mainFrame.setTitle ("Zumby");
mainFrame.setLayout(null);
// Loads the background image and stores in bg object.
try {
bg = ImageIO.read(new File("zumby.png"));
} catch (IOException e) {
}
mainFrame.setVisible(true);
}
/**
* Overrides the paint method.
* MONDAY
*/
public void paint(Graphics g)
{
// Draws the img to the BackgroundPanel.
System.out.println("paint");
g.drawImage(bg, 0, 0, null);
}
/**
*/
public MainMenu() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 800, 500);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setOpaque(false);
setContentPane(contentPane);
contentPane.setLayout(null);
//create buttons
JButton btnPlay = new JButton("PLAY");
btnPlay.setBackground(Color.BLACK);
btnPlay.setForeground(Color.WHITE);
btnPlay.setFont(font);
btnPlay.setBorder(border);
btnPlay.setFocusPainted(false);
//if "Play" is clicked
btnPlay.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent click) {
setVisible(false);
new GamePlay(); //opens up GamePlay window
}
});
btnPlay.setBounds(600, 64, 141, 61);
contentPane.add(btnPlay);
JButton btnInstructions = new JButton("INSTRUCTIONS");
btnInstructions.setBounds(600, 160, 141, 61);
btnInstructions.setBackground(Color.BLACK);
btnInstructions.setFocusPainted(false);
// btnInstructions.setEnabled(true);
contentPane.add(btnInstructions);
repaint();
pack();
setVisible(true);
}
}
Swing uses a "layering" concept for it's painting...
paint calls paintComponent, paintBorder and paintChildren. By overriding paint and failing to call super.paint, you've prevented the component from painting it's various layers.
In Swing, it is preferred to use paintComponent to provide custom painting, which allows you to paint underneath any other components that might be added to the component.
public class TestPaint01 {
public static void main(String[] args) {
new TestPaint01();
}
public TestPaint01() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Image backgroundImage;
public TestPane() {
try {
BufferedImage background = ImageIO.read(new File("/path/to/image.jpg"));
//backgroundImage = background.getScaledInstance(-1, background.getHeight() / 4, Image.SCALE_SMOOTH);
backgroundImage = background;
} catch (IOException ex) {
ex.printStackTrace();
}
setLayout(new GridBagLayout());
add(new JButton("Hello"));
}
#Override
public Dimension getPreferredSize() {
return backgroundImage == null ? super.getPreferredSize() : new Dimension(backgroundImage.getWidth(this), backgroundImage.getHeight(this));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = (getWidth() - backgroundImage.getWidth(this)) / 2;
int y = (getHeight() - backgroundImage.getHeight(this)) / 2;
g.drawImage(backgroundImage, x, y, this);
}
}
}
You might find A Closer look at the Paint Mechanism and Painting in AWT and Swing informative.
I think it's because you're overriding the paint method. It's better to override repaint, then call super.repaint(); Like this:
public void repaint(Graphics g)
{
super.repaint(g);
// Draws the img to the BackgroundPanel.
System.out.println("paint");
g.drawImage(bg, 0, 0, null);
}
Then the components get redrawn as well.
But if all you want to do is show an image as the background see here.
You're overriding paint() but don't call super.paint(). So the normal painting of the components done by the JFrame's paint() method implementation is not executed.
Since you are using Swing and JFrame the painting mechanism used to override paintComponent not paint that is usually used with applets or AWT.

JButton absolute underneath my paint() board

I have a poker game where I designed a nice pretty GUI that displays the cards and players. I did it all extending JPanel inside paint() with a lot of g2d.drawImage's and g2d.drawString()'s, with definite x and y locations.
My problem now is that I need to have a couple interactive buttons underneath it.. but whenever I try to add a JButton, it appears top and center. I've used setLocation(x, y) and setLayout(null) and everything I've seen in other replies, but none of them seem to match my need (Or at least I don't have a very well understanding of where to put it)
This is how my code is set up:
pokerserver.java
public class pokerserver extends JFrame {
public pokerserver() {
add(new drawing());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(720, 640);
setLocationRelativeTo(null);
setTitle("Poker HANGOUTS");
setResizable(false);
setVisible(true);
}
public static void main(String args[]) {
new pokerserver();
}
And then in drawing.class
public drawing() {
setFocusable(true);
setBackground(new Color(39,91,46));
setDoubleBuffered(true);
gameCards = new cards();
gameCards.shuffle();
for (int i = 0; i < 10; i++)
seats[i] = -1;
HQ = new HeadQuarters(this);
HQ.start();
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
//All my UI code
}
My last attempt was trying to add
JButton button = new JButton("TEST");
add(button);
button.setLocation(10, 500);
at the end of public drawing(). I Keep seeing things on layout management, but it's not helping me -- mainly because I'm not sure how to implement it
Here's a screenshot to help visualize what I'm talking about->
http://i.imgur.com/ttvif.png
Trying to get the button underneath. Unless there's a way to add an ActionListener to a drawImage()?
For your main panel use a BorderLayout.
Then to the "CENTER" you can add your game panel with all your custom painting.
Then create a panel and add the buttons to it. Now you can add this panel to the NORTH of the main panel.
In other words you are not restricted to using a single panel.
Also, custom painting should be done in the paintComponent() method of your panel, NOT the paint() method.
I'm not really sure what you are after, but here are two interpretations.
I suspect you want the 1st one 'Buttons over custom painting', but as a user I'd prefer the 2nd, with 'Buttons below custom painting'.
import java.awt.image.*;
import java.awt.*;
import javax.swing.*;
class PaintPanel extends JPanel {
BufferedImage bg;
PaintPanel(LayoutManager2 layout) {
super(layout);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (bg==null) {
bg = new BufferedImage(500,500,BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = bg.createGraphics();
GradientPaint gp = new GradientPaint(
0,0,Color.RED,500,500,Color.BLUE);
g2.setPaint(gp);
g2.fillRect(0,0,500,500);
g2.dispose();
}
g.drawImage(bg,0,0,getWidth(),getHeight(),this);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JPanel buttons = new JPanel(
new FlowLayout(FlowLayout.CENTER));
buttons.setOpaque(false);
buttons.add(new JButton("Start"));
buttons.add(new JButton("Stop"));
PaintPanel pp = new PaintPanel(new BorderLayout());
pp.setPreferredSize(new Dimension(200,100));
pp.add(buttons, BorderLayout.SOUTH);
JOptionPane.showMessageDialog(null,pp);
JPanel gui = new JPanel(new BorderLayout());
gui.setBackground(Color.ORANGE);
gui.add(pp, BorderLayout.CENTER);
gui.add(buttons, BorderLayout.SOUTH);
JOptionPane.showMessageDialog(null,gui);
}
});
}
}

Categories