paintComponent clears the JPanel when paint an image - java

I have this code:
package game;
import java.awt.Graphics;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Draw {
Object block;
public Draw(JFrame frame, Object object) {
this.block = object;
JPanel pane = new JPanel() {
private static final long serialVersionUID = 3869097656854760151L;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
try {
g.drawImage(ImageIO.read(new File(object.getTexture())), object.getX(), object.getY(), object.getWidth(),
object.getHeight(), null);
} catch (IOException e) {
System.err.println("Image '" + object.getTexture() + "' could not be found!");
}
}
};
frame.add(pane);
}
}
and I call the class here:
package game;
import javax.swing.JFrame;
public class Frame {
private final int X_SIZE = new vena.util.Computer().screenWidth();
private final int Y_SIZE = new vena.util.Computer().screenHeight();
public Frame() {
JFrame f = new vena.util.Frame().frame("2D Game", X_SIZE - X_SIZE / 5, Y_SIZE - Y_SIZE / 5, true, false, false,
"res/icon.png");
new Draw(f, new Object(0, 0, 100, 100, "grass"));
new Draw(f, new Object(100, 0, 100, 100, "grass"));
f.setVisible(true);
}
public static void main(String[] args) {
new Frame();
}
}
It renders when I call the image with
new Draw(f, new Object(0, 0, 100, 100, "grass"));
But when I call the image another time, next to it
new Draw(f, new Object(100, 0, 100, 100, "grass"));
it only renders the second image, and removes the first one. I have noticed that this doesn't occur when I call g.drawImage() twice in the paintComponent method. Is there a way so that I can call the Draw class as many times as I want, without clearing the JPanel?

The default layout manager of the content pane of a frame is the BorderLayout. When you add components to a BorderLayout and you don't specify a constraint the component goes to the CENTER. Only the last component added can be displayed in the CENTER.
If you want multiple components on the frame then you can change the layout manager. Try
f.setLayout( new FlowLayout() );
to see the difference.
I have noticed that this doesn't occur when I call g.drawImage() twice in the paintComponent method.
Yes, if you are trying to paint images at specific locations on the frame then you should really be overriding paintComponent() to paint each image.

Related

How do I change the position of JLabel or any other Component I add?

I am trying to create a translucent window which has no border or background other than the JLabel image's I put in it, using OverlayLayout and an extended JPanel...
My problem is when I try to add more components over the one I initially added which would be the background, I have no idea how to enable changing of the new components position.. x,y etc...
Please if possible show me what I can do and don't just point me to layoutmanagers, I need an example please if anyone is willing to show me.
Or better yet, show me what I need to do to my code in order to get the desired effect.. like changing "text" (A JLabel) position to be 10,10 ... x and y.
package core;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LayoutManager;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.OverlayLayout;
public class App {
// Window & Panel...
public JWindow frame;
public TranslucentPanel panel;
// OverlayLayout
public LayoutManager overlay;
// Components
public JLabel bg;
public JLabel test;
// Constructor
public App() {
try {
// Basics...
frame = new JWindow();
frame.setBackground(new Color(0, 0, 0, 0));
// Overlay
panel = new TranslucentPanel();
overlay = new OverlayLayout(panel);
panel.setLayout(overlay);
frame.setContentPane(panel);
// initComponents
initComponents();
// Finalize Frame
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
catch(Exception e) {
e.printStackTrace();
}
}
// Initialize Additional Components
public void initComponents() throws Exception {
test = new JLabel("test");
test.setForeground(Color.WHITE);
frame.add(test);
bg = new JLabel(new ImageIcon(ImageIO.read(getClass().getResource("/ball.png"))));
frame.add(bg);
// What must I do to be able to do this???
test.setLocation(10, 0);
}
// TranslucentPanel Class...
public class TranslucentPanel extends JPanel {
private static final long serialVersionUID = 1L;
public TranslucentPanel() {
setOpaque(false);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(0.0f));
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
}
}
}
One way would be to discard the Overlayout manager, set the TranslucentPanel's layout manager to something like BorderLayout and use the JLabel, bg as a container in of itself...
bg = new JLabel(new ImageIcon(ImageIO.read(getClass().getResource("/ball.png"))));
frame.add(bg);
// Set the layout of the JLabel
bg.setLayout(new GridBagLayout());
test = new JLabel("test");
test.setForeground(Color.WHITE);
// Add the test label to the bg JLabel...
bg.add(test);
Personally, I don't like this, as JLabel doesn't take into consideration the components (or the layout manager) when it makes it's calculations for it's preferred size.
Personally, I would create a custom background component that was responsible for painting the background image. Then, onto this, I would place the other components, using what ever combination of components and layout managers I need to produce the desired results.
Pixel perfect layouts are an illusion within modern UI design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify
After reading the following pieces from your codes :
// What must I do to be able to do this???
test.setLocation(10, 0);
If I understand correctly , you want to arrange position of your component based on custom coordinates. If so then You can use Insets class http://docs.oracle.com/javase/7/docs/api/java/awt/Insets.html to achieve that.
So you can set position of your component according to position you want
Insets insets = panel.getInsets();
Dimension size =test.getPreferredSize();
// just replace 10 & 0 according to X & Y postion you want.
test.setBounds(10 + insets.left, 0 + insets.top,size.width, size.height);
Here is you modified version:
*Note that I don't have your Icon , so I just put text on your label to help you see the result.
import java.awt.*;
import javax.swing.*;
public final class App{
// Window & Panel...
public JWindow frame;
public TranslucentPanel panel;
// OverlayLayout
public LayoutManager overlay;
// Components
public JLabel bg;
public JLabel test;
// Constructor
public App() {
try {
// Basics...
frame = new JWindow();
// Overlay
// Overlay
panel = new TranslucentPanel();
overlay = new OverlayLayout(panel);
panel.setLayout(overlay);
frame.add(panel);
initComponents();
// Finalize Frame
frame.pack();
frame.setSize(400,400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
catch(Exception e) { e.printStackTrace();}
}
// Initialize Additional Components
public void initComponents() throws Exception {
test = new JLabel("test");
test.setForeground(Color.RED);
panel.setLayout(null);
panel.add(test);
Insets insets = panel.getInsets();
Dimension size =test.getPreferredSize();
test.setBounds(10 + insets.left, 0 + insets.top,
size.width, size.height);
frame.add(panel);
}
// TranslucentPanel Class...
class TranslucentPanel extends JPanel {
private static final long serialVersionUID = 1L;
public TranslucentPanel() {
setOpaque(false);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(0.0f));
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
}
}
public static void main (String args []){
App ap = new App();
}
}
The output :
If you declare your position as test.setBounds(500 + insets.left, 10 + insets.top,size.width, size.height); then the output would be :

Why does the background image only appear after the window is resized?

I'm creating a simple window with a background image by Swing java library.
The problem is :background image appear only when you resize window.
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.LayoutManager;
import java.awt.MediaTracker;
import java.awt.event.ActionListener;
import javax.swing.*;
import java.awt.Toolkit;
public class StartWindow {
JFrame frame;
private JButton button;
private JButton button2;
public void CreateStartWindow() {
frame = Window.createwindow();
Container container = frame.getContentPane();
JpanelStart panel = new JpanelStart();
container.add(panel);
this.button = new JButton("Start");
this.button2 = new JButton("Classifica");
panel.add(button);
panel.add(button2);
}
public void addActionListener(ActionListener al) {
this.button.addActionListener(al);
this.button2.addActionListener(al);
}
public void chiudi() {
frame.dispose();
}
}
class JpanelStart extends JPanel {
private Image img;
private String path_img="img/sfondo.jpg";
public JpanelStart(){
img = Toolkit.getDefaultToolkit().createImage(path_img);
loadImage(img);
}
private void loadImage(Image img) {
try {
MediaTracker track = new MediaTracker(this);
track.addImage(img, 0);
track.waitForID(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
protected void paintComponent(Graphics g){
setOpaque(false);
g.drawImage(img,0, 0, null);
super.paintComponent(g);
}
}
Window
public class Window extends JFrame {
public static JFrame createwindow() {//fare singleton
JFrame frame = new JFrame("Battaglia navale");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(640, 640);
frame.setVisible(true);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
frame.setLocation(((int)dim.getWidth()-(int)frame.getWidth())/2,
((int)dim.getHeight()-(int)frame.getHeight())/2);
return frame;
}
;
}
If possible, call setVisible on the frame after you've added the components to it, otherwise you'll need to use revalidate and repaint.
Don't call super.paintComponent after you've painted something, as it's likely to clear Graphics context
Don't change the state of any component from with the paintComponent method, call setOpaque is bad from within the paint method is a bad idea, as the Graphics context has already being prepared assuming that the component was opaque
As has already being suggest, you should be passing this as the last parameter to drawImage, especially because of the way you load the image. Personally, I prefer to use ImageIO to load images, as it provides more details when the image fails to load....
In this line...
g.drawImage(img,0, 0, null);
... you are passing null as the ImageObserver, so your component is not told to repaint when the image is loaded. You ought to pass this.

How do I add a button to Canvas without letting the button resize?

I'm working on a login screen for my game. I have a total of two images on it. One is a splash screenshot and the other is the background image. I'm using BufferedImages to render the images to the screen.
The problem I get is that when I add a standard button to the Canvas, the button takes up the whole window, and evidently, I don't want that.
I would post a picture, but alas, I do not have "enough reputation" to do that. Here's a look at my code though:
import java.awt.Button;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.TextField;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import javax.swing.JFrame;
import javax.swing.UIManager;
public class Login extends Canvas implements Runnable {
private static final long serialVersionUID = 1L;
private static final int WIDTH = 495;
private static final int HEIGHT = 307;
private static final int SCALE = 2;
private final Dimension size = new Dimension(WIDTH * SCALE, HEIGHT * SCALE);
private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
public int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
private BufferedImage splash = new BufferedImage(315, 177, BufferedImage.TYPE_INT_RGB);
public int[] splashPixels = ((DataBufferInt) splash.getRaster().getDataBuffer()).getData();
private Thread thread;
public static boolean isRunning = false;
JFrame frame;
MainMenu menu;
Splash splashscreen;
Button login;
Button register;
TextField username;
private Login() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception exc) {
exc.printStackTrace();
}
frame = new JFrame("Game Login");
menu = new MainMenu(WIDTH, HEIGHT, "/login/login_screen.png");
splashscreen = new Splash(315, 177, "/login/splash.png");
frame.setSize(size);
frame.setVisible(true);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
setPreferredSize(size);
frame.add(this);
login = new Button("Login");
login.setBounds(0, 0, getWidth(), getHeight());
frame.add(login);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private void begin() {
createBufferStrategy(2);
thread = new Thread(this);
thread.start();
isRunning = true;
}
private void finish() throws InterruptedException {
isRunning = false;
thread.join();
}
private void updateLogin() {
for (int a = 0; a < pixels.length; a++) {
pixels[a] = menu.pixels[a];
}
}
private void renderLogin() {
BufferStrategy buffer = getBufferStrategy();
Graphics gfx = buffer.getDrawGraphics();
for (int a = 0; a < splashPixels.length; a++) {
splashPixels[a] = splashscreen.splashPixels[a];
}
gfx.drawImage(image, 0, 0, getWidth(), getHeight(), null);
gfx.drawImage(splash, 320, 37, 625, 340, null);
gfx.setColor(Color.WHITE);
gfx.drawString("Game Co © 2013", 3, (getHeight() - 4));
gfx.drawString("\"Game\" is a trademark of Blah-Blah-Blah.", (getWidth() - 268), (getHeight() - 3));
gfx.dispose();
buffer.show();
}
public void run() {
while (isRunning == true) {
updateLogin();
renderLogin();
}
try {
finish();
} catch (InterruptedException exc) {
exc.printStackTrace();
}
}
public static void main(String[] args) {
Login login = new Login();
login.begin();
}
}
Once again, my only problem is that I keep getting a enlarged button.
Thanks in advance, I know you guys are busy and whatnot and I appreciate taking the time to look over and help answer my questions.
P.S. Does anyone know how to make a password field with AWT? I'll also need that too. ;)
Solution: add your JButton (again use Swing components) First to a JPanel (which uses FlowLayout by default), and then add that to the top level window.
You could just change the layout manager for your frame to a FlowLayout so it will behave like a JPanel.
frame.setLayout(new FlowLayout());
You state:
The problem I get is that when I add a standard button to the Canvas, the button takes up the whole window, and evidently, I don't want that.
You're trying to add a component directly to a container that is using BorderLayout, likely the contentPane of a top-level window, and so by default it is added BorderLayout.CENTER and fills the container.
Solution: add your JButton (again use Swing components) First to a JPanel (which uses FlowLayout by default), and then add that to the top level window.
Again, there's no need to mix AWT and Swing components and there are in fact strong arguments not to do so. I suggest that you stick with all Swing components for your GUI.
Does anyone know how to make a password field with AWT? I'll also need that too. ;)
Again, don't use AWT but instead use a Swing JPasswordField.

JComponent.setBounds the last JComponent in the wrong place

I have been having a some issues with Swing lately. I'm trying to make something like in the image below on the fly, to illustrate the data structures for algorithms.
(source: ius.edu)
All that I was trying to do in the following class is draw out some rectangles with numbers on them. and translate them, however the last rectangle draws at 0,0. I'm stumped.
If you add the JPanel (commented out) after the loop then it draws as expected.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class DrawingRect {
public static void main(String[] args) {
DrawingRect d = new DrawingRect();
}
public DrawingRect() {
JFrame frame = new JFrame("Drawing a rect");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel swingPanel = new JPanel();
swingPanel.setPreferredSize(new Dimension(500, 500));
swingPanel.setVisible(true);
swingPanel.setLayout(new BorderLayout(0, 0));
int base = 15;
for (int i = 1; i <= 25; i++) {
Graphic re = new Graphic(i);
//translating the graphic
re.setBounds(base + 30 * i, base + 20 * i, 110, 110);
swingPanel.add(re);
}
// if say I add a JPanel in here as the last element
// then the boxes will draw correctly.
//swingPanel.add(new JPanel());
swingPanel.setPreferredSize(new Dimension(800, 600));
frame.getContentPane().add(swingPanel);
frame.pack();
frame.setVisible(true);
}
public class Graphic extends JComponent {
private static final long serialVersionUID = 1L;
private static final int PREF_W = 100;
private static final int PREF_H = 100;
int id;
public Graphic(int id) {
this.id = id;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.black);
g2.setColor(Color.black);
g2.drawRoundRect(0, 0, 30, 30, 20, 20);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Font font = new Font("", Font.PLAIN, 13);
g2.setFont(font);
g2.drawString("" + id, 15, 20);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
}
}
The default layout of JPanel is FlowLayout, but you're positioning components as if the layout were null. Moreover, you're drawing content that a nested component could do automatically.
Instead, add several JPanels containing JLabel's to a containing panel having GridLayout. Use empty panels as required. Override paintComponent() in the outer panel to draw connecting lines.
Addendum: Anytime one is tempted to use Absolute Positioning, JInternalFrame may be an alternative. Related examples may be found here and here.
Addendum: If the project grows beyond the prototype stage, also consider a Custom Layout Manager.
It is because you are using BorderLayout. You can test this by changing your add line to include a specific layout location: swingPanel.add(re,BorderLayout.SOUTH).
As for a solution, why not draw all the rectangles on a picture and update it at the end of the loop? Or is the adding to JPanel part of your demonstration?

How do I order two components in a JFrame in order for transparency and ActionListeners to work?

When adding two components to a JFrame, where one sits inside another, If I add them in the order, Fullscreen Object, then JPanel, the JPanel displays correctly, but is essentially invisible, i.e it's action listener won't work and the clicks register on the fullscreen object. If I add them the other way round The JPanel works as it should, but doesn't display correctly (It has transparent areas).
This is the code for the Frame I am adding the components to.
gameOBJ = new gameClass(width, height);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(0);
frame.add(gameOBJ.UIPanel);
frame.add(gameOBJ);
frame.validate();
frame.setUndecorated(true);
frame.setBounds(0, 0, width, height);
frame.setResizable(false);
frame.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
new exitWindow("Don't forget to save your game! \n Are you sure you want to Exit?", true);
}
});
frame.setVisible(true);
gameOBJ.start();
Here is the code for the JPanel (Stripped down for simplicity's sake)
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;
public class UserInterface extends JPanel implements ActionListener
{
private static final long serialVersionUID = 1L;
private Image image;
private int xBound = 800;
private int yBound = 177;
private JButton mainMenuButton = new JButton(new ImageIcon("res/images/MainMenuButton.gif"));
private int buttonWidth = 179;
private int buttonHeight = 52;
public UserInterface()
{
this.setLayout(null);
this.image = new ImageIcon("res/images/UIPanelImage.gif").getImage();
this.setOpaque(false);
this.setSize(this.xBound, this.yBound);
mainThreeButtons(); //ONLY ONE SHOWN FOR SIMPLICITY
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image, 0, 0, this); //IMAGE CONTAINS TRANSPARENCY
}
#Override
public void actionPerformed(ActionEvent event)
{
else if (event.getSource() == mainMenuButton)
{
new mainMenuWindow();
}
}
private void mainThreeButtons()
{
this.add(mainMenuButton);
mainMenuButton.addActionListener(this);
//mainMenuButton.setOpaque(false);
mainMenuButton.setBorderPainted(false);
mainMenuButton.setContentAreaFilled(false);
mainMenuButton.setBounds(617, 6, buttonWidth, buttonHeight);
}
}
I would show an image but I'm not allowed to, The area which is meant to be transparent isn't showing the frame, because it is grey, whatever I set as the Frame's background, OR the panel's background, as again it is grey whatever I set the panel's background colour as.
You probably want to use JLabel instead of JPanel. I know it sounds a bit unintuitive, but I'm not sure JPanel is suited to the purpose you are using it for. Also, JLabel can have a native ImageIcon set, so try using that.
public UserInterface() { // extends JLabel
this.setImageIcon(new ImageIcon("res/images/UIPanelImage.gif"));
// or super(~imageicon~)
}
Unlikely, but it could be that the image is not yet loaded when it gets drawn. You should use MediaTracker to manage that more carefully (although I'm not sure ImageIcon if takes care of this for you).
final static protected MediaTracker mediatracker = new MediaTracker(new Canvas());
static protected void checkImageIsReady(Image i) {
mediatracker.addImage(i, 1);
try {
mediatracker.waitForAll();
} catch (InterruptedException e) { }
mediatracker.removeImage(i);
}

Categories

Resources