public class bioscope extends Component{
static int width;
static int height;
public void paint(Graphics g){
try {
BufferedImage crow = ImageIO.read(new File("photos/houseCrow.jpg"));
this.width = crow.getWidth();
this.height = crow.getHeight();
System.out.println(this.height);
System.out.println(this.width);
g.drawImage(crow, 0, 0, null);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Bioscope: Have a peek!");
frame.getContentPane().add(new bioscope());
frame.setVisible(true);
frame.setSize(bioscope.width, bioscope.height);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//frame.setResizable(false);
System.out.println(bioscope.height);
System.out.println(bioscope.width);
}
}
Output window has zero height and width, which is frustrating but still seems explicable. What stuns me is the output of println commands. I expected this to be a four line output: 492,640,492,640. But it prints out 0,0 first, and apparently halts. But go fullscreen, and 492,640 will be appended at the printout! Now you-know-who would invoke println every time you go fullscreen, and another 492,640 would be appended. Sort of similar appendings will occur if you minimize or try to resize JFrame window.
Why this would happen, and why JFrame window wasn't of dimensions 492,640 at the first place? The image was attached successfully, though, as could be seen if I resized the window.
I'm not sure if you expect your two static fields width and height to have any effect on the actual dimensions of the component, or if you're just using them for debugging. The static fields you have declared shadow the width and height fields in Component. It would be more appropriate to use getWidth() and getHeight() to track the actual values used by the super class.
It prints 0, 0 first because the static fields are not initialized until the first paint. The paint method is only called when the frame is redrawn, which is why you see the log line every time you change the size of the window.
Try this:
public class bioscope extends JComponent {
transient BufferedImage crow;
public bioscope() {
try {
crow = ImageIO.read(new File("photos/houseCrow.jpg"));
} catch (IOException e) {
throw new ExceptionInInitializerError(e);
}
setPreferredSize(new Dimension(crow.getWidth(), crow.getHeight()));
}
public void paint(Graphics g){
g.drawImage(crow, 0, 0, null);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Bioscope: Have a peek!");
bioscope bioscope = new bioscope();
frame.getContentPane().add(bioscope);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
frame.setResizable(false);
}
}
Related
I'm having an issue instantiating a class in Java, essentially it generates a new world every tick, which is a bit frustrating when the program runs.
Whereas all I need to do is instantiate it then access a variable inside the class.
Here's the code:
Background.java
public class Background extends UserView {
private BufferedImage bg;
private static Game game;
public Background(World w, int width, int height) {
super(w, width, height);
try {
bg = ImageIO.read(new File("data/background.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void paintBackground(Graphics2D g) {
super.paintBackground(g);
game = new Game();
g.drawImage(bg, 0, 0, this);
int level = game.getLevel();
g.drawString("Level: " + level, 25, 25);
}
}
Game.java
public Game() {
// make the world
level = 1;
world = new Level1();
world.populate(this);
// make a view
view = new Background(world, 500, 500);
// uncomment this to draw a 1-metre grid over the view
// view.setGridResolution(1);
// display the view in a frame
JFrame frame = new JFrame("Save the Princess");
// quit the application when the game window is closed
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
// display the world in the window
frame.add(view);
// don't let the game window be resized
frame.setResizable(false);
// size the game window to fit the world view
frame.pack();
// make the window visible
frame.setVisible(true);
// get keyboard focus
frame.requestFocus();
// give keyboard focus to the frame whenever the mouse enters the view
view.addMouseListener(new GiveFocus(frame));
controller = new Controller(world.getPlayer());
frame.addKeyListener(controller);
// start!
world.start();
}
/** Run the game. */
public static void main(String[] args) {
new Game();
}
Any help would be appreciated! Thank you!
Well you probably need to think about the class concept nad its dependencies, but this is the easiest and fastest approach in your case to keep only one instance of Game :
public class Background extends UserView {
private BufferedImage bg;
private static Game game = new Game();
public Background(World w, int width, int height) {
super(w, width, height);
try {
bg = ImageIO.read(new File("data/background.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void paintBackground(Graphics2D g) {
super.paintBackground(g);
g.drawImage(bg, 0, 0, this);
int level = game.getLevel();
g.drawString("Level: " + level, 25, 25);
}
}
If you add more code and say what you want and what you get, we can say you more about it.
I am quite new to Java and Swing, and this is also my first post so sorry if it doesn't make too much sense.
What I am trying to do is when I click on a JPanel, I want it to add a circle where I click. At the moment, all that seems to happen is when I click, a small grey square appears inside the JPanel I want to add to, but I can't seem to find any way of making it draw as a circle.
I have a class that extends JPanel called "Ball" which is what is being added when I click.
At the moment, I am not too worried about it being in the correct location, just for it to draw the ball correctly.
Below is the code for my "Ball" class:
package paintsliders;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
class Ball extends JPanel{
private int x,y,w,h;
//I will use this constructor to put the ball in the correct location later.
Ball(){
/*this.w = 100;
this.h = 100;
this.x = 200;
this.y = 200;*/
}
//draw the ball
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawOval(200,200,10,10);
g.setColor(Color.RED);
}
}
I can kind of guess that it is something to do with the paintComponent method, but everywhere I have looked doesn't seem to have a solution for me.
Any help would be great, thanks!
The Graphcis context has already been translated to meet the x/y location that the component should appear within it's parent container, this means that the top, left corner of the Graphics context within the paintComponent method is actually 0x0.
You need to define some size for the ball, you're painting at 10x10, which would suggest that your ball component should return a preferredSize of 10x10
public Dimension getPreferredSize() {
return new Dimension(10, 10);
}
You will become responsible for providing appropriate layout details to the ball when it's added to the parent container...
public void mouseClicked(MouseEvent evt) {
Point p = evt.getPoint();
Ball ball = new Ball();
Dimension size = ball.getPreferredSize();
ball.setBounds(new Rectangle(p, size));
add(ball);
}
This, of course, assumes you have a null layout set for the parent container
UPDATED
Something like...
public class PaintBalls {
public static void main(String[] args) {
new PaintBalls();
}
public PaintBalls() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new Board());
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Board extends JPanel {
public Board() {
setLayout(null);
setBackground(Color.WHITE);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
Point p = e.getPoint();
Ball ball = new Ball();
Dimension size = ball.getPreferredSize();
p.x -= size.width / 2;
p.y -= size.height / 2;
ball.setBounds(new Rectangle(p, size));
add(ball);
repaint();
}
});
}
}
public class Ball extends JPanel {
public Ball() {
setOpaque(false);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(10, 10);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.fillOval(0, 0, 10, 10);
g2d.dispose();
}
}
}
You probably have a main JPanel where you click.
I would rather design the main panel to handle the mouse click and the Ball class to be a simple Object that defines a drawBall(Graphics g, int x, int y) method that knows how to paint a Ball. This would be called by the paintComponent() method in the main panel. In the main panel, you handle the mouse click, create an object of type Ball and call repaint(). Inside the paintComponent() you call ball.drawBall().
Ive got a JFrame and set the LayoutManager to BorderLayout and then proceeded to add my JLabel with an image. However when i resize the frame the JLabel doesnt resize. I have not added any components to North, S, E and so on. I was hoping to simply have the image inside the label fill up the entire frame leaving my menu in tact of course.
Forgive me if this seems arrogant, but I have nothing else to go on.
I did a quick sample
See the red line around the image, that's the JLabel's border. As you can see, the label is been re-sized to fill the entire area.
This is the code I used to produce the sample
public class LayoutFrame extends JFrame {
public LayoutFrame() throws HeadlessException {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Image image = null;
URL url = getClass().getResource("/layout/issue78.jpg");
try {
image = ImageIO.read(url);
} catch (IOException ex) {
ex.printStackTrace();
}
JLabel label = new JLabel(new ImageIcon(image));
label.setHorizontalAlignment(JLabel.CENTER);
label.setVerticalAlignment(JLabel.CENTER);
label.setBorder(new LineBorder(Color.RED, 4));
setLayout(new BorderLayout());
add(label);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
LayoutFrame frame = new LayoutFrame();
frame.setSize(200, 200);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
});
}
}
Obviously, you'll need to supply your own image ;).
Don't forget, the label WON'T scale the content for you, if that's your goal, you'll need to implement your own component to achieve this.
If you're still having problems, I would suggest (in the absence of further evidence) that your label may not be in the container you think it is or the containers layout manager is not what you think it is.
UPDATE
I don't know why you're having issues with components going missing or issues with you menu. Are mixing heavy and light weight components??
Sample with menu bar
After reading your question a little closer, I've devised a simple resizing image pane sample. For speed, I've relied on my libraries, but it should be reasonably easy to implementation your own code in place of my calls
public class ImagePane extends JPanel {
protected static final Object RESIZE_LOCK = new Object();
private BufferedImage image;
private BufferedImage scaledImage;
private Timer resizeTimer;
public ImagePane() {
URL url = getClass().getResource("/layout/issue78.jpg");
try {
image = ImageIO.read(url);
} catch (IOException ex) {
ex.printStackTrace();
}
resizeTimer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Simple thread factory to start a slightly lower
// priority thread.
CoreThreadFactory.getUIInstance().execute(new ResizeTask());
}
});
resizeTimer.setCoalesce(true);
resizeTimer.setRepeats(false);
}
#Override
public void setBounds(int x, int y, int width, int height) {
super.setBounds(x, y, width, height);
resizeTimer.restart();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if (scaledImage != null) {
// This simply returns a rectangle that takes into consideration
//the containers insets
Rectangle safeBounds = UIUtilities.getSafeBounds(this);
System.out.println("scaledImage = " + scaledImage.getWidth() + "x" + scaledImage.getWidth());
int x = ((safeBounds.width - scaledImage.getWidth()) / 2) + safeBounds.x;
int y = ((safeBounds.height - scaledImage.getHeight()) / 2) + safeBounds.y;
g2d.drawImage(scaledImage, x, y, this);
}
}
protected class ResizeTask implements Runnable {
#Override
public void run() {
synchronized (RESIZE_LOCK) {
if (image != null) {
int width = getWidth();
int height = getHeight();
System.out.println("width = " + width);
System.out.println("height = " + height);
// A simple divide and conquer resize implementation
// this will scale the image so that it will fit within
// the supplied bounds
scaledImage = ImageUtilities.getScaledInstanceToFit(image, new Dimension(width, height), ImageUtilities.RenderQuality.High);
System.out.println("scaledImage = " + scaledImage.getWidth() + "x" + scaledImage.getWidth());
repaint(); // this is one of the few thread safe calls
}
}
}
}
}
Best option is to sub class ImageIcon and override its paintIcon method to simply paint the image using the Graphics.paint( x, y, width, height ...).
im trying to set a background image on a JPanel that resizes with the panel. I have no problem in showing the picture, but as soon as I use:
background = background.getScaledInstance(300, -1, Image.SCALE_SMOOTH );
nothing is shown. Any ideas on why?
The code:
import javax.swing.*;
import java.awt.*;
public class LoginJPanel extends JPanel
{
private Image background;
public LoginJPanel()
{
super();
background = new ImageIcon("C:\\ASYS\\Stories\\Authentication UI\\AVDsplashscreen_tiny.jpg").getImage();
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
background = background.getScaledInstance(300, -1, Image.SCALE_SMOOTH );
g.drawImage(background, 0, 0, this);
}
public static void main (String[] args)
{
LoginJPanel ip = new LoginJPanel();
JFrame jf = new JFrame ();
jf.setLayout (new BorderLayout ());
jf.add (ip, BorderLayout.CENTER);
jf.setSize (1000, 600);
jf.setLocation (150, 150);
jf.setVisible (true);
jf.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
}
}
In the end what I made is (as suggested by the comments) to move the scaling outside the paint method. I created a public method which is called from the parent components to notify the panel about the new size, and scale the picture acording to that:
public void initSize(int _width, int _height)
{
int h = background.getHeight(null);
int w = background.getWidth(null);
if (w - _width > h - _height)
{
scaleVertically(_width, _height);
}
else
{
scaleHorizontally(_width, _height);
}
}
I guess I should do this on some listener because this is not very elegant, but I didn't know how to do so.
Please note I haven't tested this on a Windows-machine only on a Mac-machine. I'm not so sure whether this also occurs on a Windows-machine...
When I resize my Java-application the content is invisible. I already found a way to fix it after resizing it, but not while the user is resizing the window.
I'm not using Swing or something because it makes my binary so slow (in my opinion).
The structure is like this:
Frame My main-window
Container Content view of main-window
Container-based subviews that including the paint(Graphics g)-method
I've added all listeners to My main-window and now I'm able to redraw the Content-view after resizing the window.
public void componentResized(ComponentEvent e) {
this.contentView.paint(this.contentView.getGraphics());
}
I am beware of the fact using the paint(getGraphics())-method isn't a really good way to do this, but since the repaint()-method doesn't do anything at all, it's the only working possibility.
While resizing, all painted content becomes invisible. However, when I add a Button-instance to my Content-view and resize my Main-window, the button doesn't get invisible.
I am able to trace the 'live'-resize event:
public void componentMoved(ComponentEvent e) {
System.out.println("Live-resize");
}
When I start resizing this method is not being called.
While resizing it generates "Live-resize" in my log every single pixel I resize the window.
When I stop resizing this method is not being called, the componentResized-method does.
When I add my repaint-method (or the official repaint-method) to the 'live'-resize event like this, I still get the output, however, it's not repainting or something
public void componentMoved(ComponentEvent e) {
System.out.println("Live-resize");
this.contentView.paint(this.contentView.getGraphics());
}
Or
public void componentMoved(ComponentEvent e) {
System.out.println("Live-resize");
this.contentView.repaint();
}
When I minimize my application to the dock and maximize the application again, the same thing happens, I guess that the same code is needed to fix this.
I'm not using Graphics2D or something, just Graphics.
Could you please explain me how I can repaint the views?
Thanks in advance,
Tim
For reference, here is the same program using Swing. Because JPanel is double buffered, it doesn't flicker as the mouse is released after resizing.
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;
public class SwingPaint {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.add(new CirclePanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private static class CirclePanel extends JPanel {
private static final Random r = new Random();
public CirclePanel() {
this.setPreferredSize(new Dimension(320, 240));
this.setForeground(new Color(r.nextInt()));
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
CirclePanel.this.update();
}
});
}
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);
}
}
}
I'm more familiar with Swing, but the article Painting in AWT and Swing distinguishes between system- and application-triggered painting. The example below shows how the system invokes paint() as the window is resized, while the application invokes repaint(), which calls update(), in response to a mouse event. The behavior is cross-platform.
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class AWTPaint {
public static void main(String[] args) {
Frame frame = new Frame();
frame.add(new CirclePanel());
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.pack();
frame.setVisible(true);
}
private static class CirclePanel extends Panel {
private static final Random r = new Random();
public CirclePanel() {
this.setPreferredSize(new Dimension(320, 240));
this.setForeground(new Color(r.nextInt()));
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
CirclePanel.this.repaint();
}
});
}
#Override
public void update(Graphics g) {
this.setForeground(new Color(r.nextInt()));
}
#Override
public void paint(Graphics 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);
}
}
}
Okay, I finally fixed it.
Instead of redrawing it every time in the paint(Graphics g)-method, you need to buffer the output and only redraw that image (I kinda hoped Java would be already doing that, just like Obj-C).
public BufferedImage buffer;
public void redraw() {
buffer = new BufferedImage(
200, // height
300, // width
BufferedImage.TYPE_4BYTE_ABGR); // ABGR = RGBA, 4-byte (r, g, b, a) per pixel
Graphics g = buffer.getGraphics();
// do your drawing here
if (this.getGraphics()) {
// 'this' is already shown, so it needs a redraw
this.paint(this.getGraphics()); // little hack
}
}
public void update(Graphics g) {
this.paint(g);
}
public void paint(Graphics g) {
g.drawImage(buffer, 0, 0, this);
}
Now, when you minimize the window and maximize it again, the paintings remain. Only, the window's flickering now for .1-second or so, but I don't really care about that.