Java - JFrame, JPanel, Layout, and Clipping - java

I have three questions/problems. (NOTE - I don't have enough reputation to post pics, so I linked them. And I needed to obfuscate them...)
1) I created a panel that holds my game graphics (the player area). The panel is supposed to be 800x800 and clip everything that lies below and to the right. But when I add the graphics panel to a JFrame, it doesn't clip. So images go over on the right and left.
This is a picture of how to game starts. Ideally, the graphics would start in this rectangle the whole time:
Picture #1: http://i.stack.imgur.com/idL8f.png
Now, here's what happens when I press play to start.
Picture #2: http://i.stack.imgur.com/dxtbe.png
How can I set the panel/frame so that the graphics only occupy 800x800 (like the first picture) and everything else is clipped?
2) I'm a bit confused about how I can set up the JFrame. This is how I want it to be layed out:
Picture #3: http://i.stack.imgur.com/ZyJS5.png
How would you lay the JFrame/Panels out? I was thinking BorderLayout, but I'm not certain it would work out.
3) For this game, my class that extends JFrame also contains main(). Is this bad practice?** Are you supposed to not extend JFrame on the main class?

The easiest way to get an 800x800 panel is to use setPreferredSize() and then pack() the JFrame that contains is. Conveniently, pack() "Causes this Window to be sized to fit the preferred size and layouts of its subcomponents."
2). See A Visual Guide to Layout Managers for layout suggestions. You can use nested panels to achieve your desired layout.
3). There's nothing wrong with extending JFrame, but there's little point unless you are modifying the behavior of JFrame. In contrast, JPanel is a convenient container for grouping components; it was designed to be extended. You might examine this example in that regard.
Addendum:
I don't want the panel to show anything but the 800 pixels in the x and y direction.
You can override paintComponent() and copy whatever portion of the image is desired. In the example below, g.drawImage(img, 0, 0, null) draws the top-left 800 pixels of the image, while g.drawImage(img, 0, 0, getWidth(), getHeight(), null) scales the image the panel's size. Note that f.setResizable(false) prevents changing the window's size.
Addendum: You can also copy arbitrary portions of the source image to arbitrary areas of the
the destination panel, as shown below. Also consider overriding getPreferredSize(), as suggested here.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** #see http://stackoverflow.com/q/3851847 */
public class MyPanel extends JPanel {
private BufferedImage img;
public MyPanel() {
this.setPreferredSize(new Dimension(800, 800));
try {
img = ImageIO.read(new File("../scratch/image.png"));
} catch (IOException ex) {
ex.printStackTrace(System.err);
}
}
#Override
protected void paintComponent(Graphics g) {
// g.drawImage(img, 0, 0, 800, 800, null);
// g.drawImage(img, 0, 0, getWidth(), getHeight(), null);
g.drawImage(img, 0, 0, 800, 800, 0, 0, 800, 800, this);
}
private void display() {
JFrame f = new JFrame("MyPanel");
// f.setResizable(false);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new MyPanel().display();
}
});
}
}

Related

JPanel Graphics not drawing anything (Java) [duplicate]

This question already has an answer here:
Can't draw to JPanel with getGraphics
(1 answer)
Closed 4 years ago.
I'm having trouble getting the graphics of my JPanel to work. It refuses to draw anything, regardless of anything I've tried and anything I can find on the internet.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.Timer;
import java.util.*;
import java.io.*;
public class Mandelbrot{
public static void main(String[] args){
JFrame win=new JFrame();
JPanel dis=new JPanel();
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
win.setResizable(false);
win.setVisible(true);
win.add(dis);
dis.setPreferredSize(new Dimension(1000,500));
win.pack();
Graphics g=dis.getGraphics();
g.setColor(Color.red);
g.fillRect(0, 0, 100, 100);
}
}
Posting as an answer because I ran out of comment room:
Note:
If you need to be constantly changing things, then a JPanel is probably not your best option. I recommend you rethink what you are trying to do because you should probably use a Canvas or paint to a bunch of different labels/glass panes and overlay them however you want, this will allow you to have moving components/animations in a foreground item, and make different changes to the background item.
Alternatively, you can make the JPanel draw a buffered image, or you can store a list of items to paint, and you can paint them each time. For the buffered image method you can directly edit and draw to the buffered image every time you need to make a change.
Below is an example of how to use the buffered image method.
First create a custom JPanel in a new class:
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
public class DrawPanel extends JPanel{
public BufferedImage canvas = new BufferedImage(panelWidth, panelHeight, BufferedImage.TYPE_INT_ARGB);
#Override
public void paintComponent(Graphics g){
//Draw the canvas
g.drawImage(canvas, 0, 0, this);
}
}
Now in your main method you can replace JPanel dis=new JPanel() with this:
DrawPanel dis = new DrawPanel();
Graphics g=dis.canvas.getGraphics();
g.setColor(Color.red);
g.fillRect(0, 0, 100, 100);
Note how I use dis.canvas to get the graphics of the bufferedImage instead of the graphics of the JPanel.
It's as simple as that.
As per Andrews comment. You should consider extending a JLabel instead of a JPanel, it is much more lightweight, and easier to update using label.repaint();.
public static void main(String... args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
JPanel panel = new JPanel() {
#Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.red);
g.fillRect(0, 0, 100, 100);
}
};
panel.setPreferredSize(new Dimension(640, 480));
frame.add(panel);
frame.setVisible(true);
frame.pack();
}
Just an example - you should create a new Class subclassing JPanel, see Painting in AWT and Swing.

How do I switch a JFrame from being decorated to being not decorated?

I want a game I am making to go fullscreen. I have the game being drawn in a JPanel and added to a JFrame.
I am currently using graphicsDevice.setFullScreenWindow(jframe) to set the JFrame as fullscreen.
The JFrame is decorated. When I set it fullscreen, there is space left for the decoration, but I want it to take up the whole screen. When I have the JFrame as undecorated, it takes up the whole screen when fullscreen.
I want the JFrame to be decorated when in windowed mode, but when in fullscreen mode, I want the screen to be completely used.
I have tried setting the JFrame to be not visible, then changing the decoration and setting it back to visible. I have also tried setting the original to have decoration, making a new JFrame with no decoration and fullscreen with the JPanel added.
I'm not sure but I think you cannot change the setUndecorated() property after a JFrame has become visible. As a result, one solution would be to have two frames. One windowed, one fullscreen. Swap between the frames when you want to toggle fullscreen.
Since you're drawing things onto a JPanel, the solution is to create a draw() method where the drawing will happen. Things do get a little more complicated if you wish to add Swing components though. Anyway, try this out.
Code:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class FullScreenExample {
public static void main(String[] args) throws InterruptedException {
new FullScreenExample();
}
public FullScreenExample() throws InterruptedException {
// Full screen frame set-up.
JFrame frameFullScr = new JFrame();
frameFullScr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frameFullScr.setUndecorated(true);
frameFullScr.add(new JPanel() {
#Override
public void paintComponent(Graphics g) {
if(frameFullScr.isVisible()) draw(this, g);
}
});
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(frameFullScr);
// Windowed frame set-up.
JFrame frameWin = new JFrame();
frameWin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frameWin.setSize(512, 512);
frameWin.add(new JPanel() {
#Override
public void paintComponent(Graphics g) {
if(frameWin.isVisible()) draw(this, g);
}
});
Thread.sleep(5000);
// Now let's go into a window...
frameFullScr.setVisible(false);
frameWin.setVisible(true);
}
private void draw(JComponent component, Graphics g) {
g.setColor(Color.ORANGE);
g.fillRect(0, 0, component.getWidth(), component.getHeight());
g.setColor(Color.RED);
g.drawOval(32, 32, 256, 256);
}
}
Just some unrelated advice: In case you run into performance problems, try looking into AWT's BufferStrategy. It is more optimized better accelerated by the graphics card. You can expect a +25% to 75% improvement in framerates.

how to add a background to a jpanel

I would like to add a jlabel to a jpanel I've used this code
JLabel background=new JLabel(new ImageIcon("Myimage.jpg"));
panel.add(background);
frame.setContentPane(panel);
the image is sized with the minimized window not the maximized one, so when I maximize the window it shows an empty space which I don't want. I would like a background for the jpanel I added
Just override the paintComponent(Graphics g) : void of your JPanel. The following solution will scale your image always to the panel size.
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JPanel;
public class ImagePanel extends JPanel {
private Image image;
public void setBackgroundImage(Image image) {
this.image = image;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(this.image, 0, 0, this.getWidth(), this.getHeight(), this);
}
}
For performance and quality aspects it could be also useful to set some RenderingHints.For more information see Controlling Rendering Quality.
Please consider to search first before you ask. Maybe this answer adresses your problem How to fit Image size to JFrame Size? as well?

Java graphics PaintComponent problems. Can't seem to find the mistake

I'm making a program for drawing out an Image and it seems I've made a mistake and my program just doesn't want to draw out the image. Can someone please point out the mistake for mi because i really don't see it.
package basic_game_programing;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Practise extends JPanel {
public Image image;
//#####PAINT__FUNCTION#####
public void PaintComponent(Graphics g){
super.paintComponent(g);
ImageIcon character = new ImageIcon("C:/Documents and Settings/Josip/Desktop/game Šlije/CompletedBlueGuy.PNG");
image = character.getImage();
g.drawImage(image,20,20,null);
g.fillRect(20, 20, 100, 100);
}
//######MAIN__FUCTION#######
public static void main(String[]args){
Practise panel = new Practise();
//SETTING UP THE FRAME
JFrame frame = new JFrame();
//
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500,500);
frame.add(panel);
//SETTING UP THE PANEL
//
}
}
You're miscapitalizing paintComponent by using PaintComponent instead (note the first "P").
So Change PaintComponent to paintComponent.
Use the #Override annotation above the method to let the compiler tell you when you're making this kind of mistake.
Never read an image into a painting method since this slows down a method that needs to be fast, and makes the image be read in over and over when one read is enough.
The method should be protected not public.
Use ImageIO.read(...) to read in your image, and use resources and relative path within the jar file, rather than use files or ImageIcons.
Don't call setVisible(true) on the JFrame until after adding all components, else some might not show.
Do read the tutorials as most all of this is well explained there.
e.g.,
public class Practise extends JPanel {
private Image image;
public Practice() {
// read in your image here
image = ImageIO.read(.......); // fill in the ...
}
#Override // use override to have the compiler warn you of mistakes
protected void paintComponent(Graphics g){
super.paintComponent(g);
// never read within a painting method
// ImageIcon character = new ImageIcon("C:/Documents and Settings/Josip/Desktop/game Šlije/CompletedBlueGuy.PNG");
// image = character.getImage();
g.drawImage(image, 20, 20, this);
g.fillRect(20, 20, 100, 100);
}
}

Making image scrollable in JFrame contentpane

I am trying to display a large image inside a JFrame's contentpane. I would like to make the image or contentpane scrollable as the image is large. I tried to do it using Jscrollpane and add it into the contentpane but it didn't work. Did some searching for solution but end up failed to find one. Can someone guide me? My code are below
FinalEnvironment.java
package environment;
import java.awt.*;
import java.net.URL;
import javax.swing.*;
public class FinalEnvironment{
public FinalEnvironment(){
Image Eastlake;
URL EastlakeURL = null;
EastlakeURL = FinalEnvironment.class.getResource("/image1/eastlake_night.png");
Eastlake = Toolkit.getDefaultToolkit().getImage(EastlakeURL);
JFrame frame = new JFrame("UniCat World");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 600);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
JMenuBar yellowMenuBar = new JMenuBar();
Map map = new Map(800, 550, Eastlake);
yellowMenuBar.setOpaque(true);
yellowMenuBar.setBackground(Color.YELLOW);
yellowMenuBar.setPreferredSize(new Dimension(800, 50));
frame.setJMenuBar(yellowMenuBar);
JScrollPane scroller = new JScrollPane(map);
scroller.setAutoscrolls(true);
scroller.setPreferredSize(new Dimension(800, 550));
frame.getContentPane().add(scroller, BorderLayout.CENTER);
frame.setSize(800, 600);
frame.setVisible(true);
}
public static void main(String[] args){
FinalEnvironment fe = new FinalEnvironment();
}
}
Here is my map.java
package environment;
import java.awt.*;
import javax.swing.*;
public class Map extends JPanel{
private int width;
private int height;
private Image img;
public Map(int width, int height, Image img){
this.width = width;
this.height = height;
this.img = img;
}
protected void paintComponent(Graphics g)
{
super.paintComponents(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(img,0,0,2624,1696,null);
}
}
Lastly, I would like to place Jbuttons on top of this image. Should I call a Rectangle and place it on top the image in the contentpane which then I use Point to position my buttons or should I straight away use the image or the component itself to do it? I need the button to be able to synchronize with the image when it is scrolled instead of static in the contentpane.
Thanks
What I would do here:
1.Have a panel (canvas) which only responsibility is to paint a given image independent of the real image size in overridden method paintComponent()
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
2.Make sure the canvas preferred size equals to image real size.
3.Have a second panel which will serve as content pane of a frame.
4.In it you will set a JScrollPane as its centre.
5.In the scroll pane viewport will be the component from step 1.
6.Add your button to canvas panel from step 1. It will be scrolled together with the image.
7.Add the content pane, the panel from step 3, to a frame, and run the application.
EDIT:
Code sample with button added to canvas, which stays always in its place, independent of scroll position or frame size.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
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;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class ScrollImageTest extends JPanel {
private static final long serialVersionUID = 1L;
private BufferedImage image;
private JPanel canvas;
public ScrollImageTest() {
try {
this.image = ImageIO.read(new URL("http://interviewpenguin.com/wp-content/uploads/2011/06/java-programmers-brain.jpg"));
}catch(IOException ex) {
Logger.getLogger(ScrollImageTest.class.getName()).log(Level.SEVERE, null, ex);
}
this.canvas = new JPanel() {
private static final long serialVersionUID = 1L;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
};
canvas.add(new JButton("Currently I do nothing"));
canvas.setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));
JScrollPane sp = new JScrollPane(canvas);
setLayout(new BorderLayout());
add(sp, BorderLayout.CENTER);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JPanel p = new ScrollImageTest();
JFrame f = new JFrame();
f.setContentPane(p);
f.setSize(400, 300);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
});
}
}
What if you use your dimensions to set the Map's preferred size. For instance, give Map this method:
// method in the Map class
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
This way the Map JPanel will take up the necessary room to show the entire image. Also, why does your drawImage method in the paintComponent method have the large magic numbers? Why not use the width and height there as well? Edit 1: or don't even specify the image size as Boro suggests in his answer (1+ to him).
Why is everybody reinventing the wheel??? There is no need for a custom panel to paint the image!!!
All you need to do is create a JLabel and add an ImageIcon to the label and you won't have a problem. The label will:
paint the image at (0, 0) at its original size (which is exactly what the custom code is doing).
determine the preferred size of the image based on the image size. Now scrolling will happen automatically.
Also there is rarely any reason to use the setPreferredSize() method since all components have a default preferred size. So you should not set the default size of the menu bar. The only time I set a preferred size would be on the JScrollPane. This will allow the frame to be packed at a reasonable size and then scrollbars will appear automatically based on the size of the image in the label.
In addition to other helpful answers, you might like studying this example that uses mouse gestures to scroll arbitrary content.

Categories