Graphics doesn't draw image in java - java

I have this class which should draw an image.
package ro.adlabs.imnuriAZSMR.UIClases;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
public class JImage extends JPanel {
private BufferedImage image;
private int height;
private int width;
public JImage(String imagePath,int height,int width) {
try {
image = ImageIO.read(getClass().getResourceAsStream(imagePath));
} catch (IOException ex) {
ex.printStackTrace();
}
this.width = width;
this.height = height;
}
public JImage(String imagePath,int size){
new JImage(imagePath,size,size);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, width, height, this);
}
}
And this Class which show an About Dialog:
package ro.adlabs.imnuriAZSMR.UIClases;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class AboutDialog extends JDialog {
public AboutDialog() {
setTitle("About");
setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
JLabel name = new JLabel("<html><div style='text-align: center;'>Aceasta aplicatie e dezvoltata sub Termenii si Conditiile ADLabs.</div></html>");
JLabel copyright = new JLabel("© ADLabs - www.adlabs.ro");
name.setAlignmentX(0.5f);
copyright.setAlignmentX(0.5f);
add(name);
add(new JImage("../ico/appicon_200x200.png",50));
add(copyright);
JButton close = new JButton("Close");
close.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
dispose();
}
});
close.setAlignmentX(0.5f);
add(close);
setModalityType(ModalityType.APPLICATION_MODAL);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setSize(300, 200);
}
}
I add to the dialog the Image which is the app logo. First time when I created the JImage class it worked, it drawn the picture. Then I added the method:
setSize(width+20,height+20);
to the Jpanel in the JImage class and when I ran the program again it didn't draw the image. Then anything I did it didn't solve this wierd bug.
Anyone has any ideea? What am I doing wrong?

Graphics doesn't draw image in java
You are using a BoxLayout. A BoxLayout will use the preferred size information of the panel when doing the layout. Your preferred size is (0, 0) so there is nothing to paint.
You need to override the getPreferredSize() method of the panel when you do custom painting to return the size of your component so layout managers can do their job.
However, as already mention there is no need to create a custom class as you can just use a JLabel to display an image. The only time you do custom painting is when you need to somehow modify the image when it is painted.

You are using a resource path, not a file system path. Such a path may not contain .. and is either relative to the package directory of the class or absolute.
new JImage("../ico/appicon_200x200.png", 50)
should become something like:
new JImage("/ro/adlabs/imnuriAZSMR/ico/appicon_200x200.png", 50)
Also:
public JImage(String imagePath, int size){
new JImage(imagePath, size, size);
}
should be
public JImage(String imagePath, int size){
this(imagePath, size, size);
}

Related

Wrong RGB values are returned

I am trying to get the RGB value of a pixel of an image. (The pixel that the mouse is over). Here is the code which adds the image and the code which attends to the mouse ::
/// I got this code somewhere from StackOverFlow;
///Couldn't find the link;
label = new JLabel();
try {
// THIS PICTURE IS NOT MINE
targetImg = ImageIO.read(new java.net.URL("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSFAhy8GM1F3_TyPjG7ezbeFRUy00PN4hpJCA&usqp=CAU"));
label.setIcon(new ImageIcon(rescale(targetImg)));
panel1.add(label);
fields = new JPanel();
fields.setBorder(new EmptyBorder(5, 5, 5, 5));
red = new JTextField(3);
green = new JTextField(3);
blue = new JTextField(3);
red.setEditable(false);
green.setEditable(false);
blue.setEditable(false);
fields.add(red);
fields.add(green);
fields.add(blue);
panel1.add(fields);
label.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
try{
int packedInt = targetImg.getRGB(e.getX(), e.getY());
Color color = new Color(packedInt, true);
fields.setBackground(color);
red.setText(Integer.toString(color.getRed()));
green.setText(Integer.toString(color.getGreen()));
blue.setText(Integer.toString(color.getBlue()));
}
catch (ArrayIndexOutOfBoundsException exception) { }
}
});
} catch (IOException ex) {
ex.printStackTrace();
}
This runs and compiles fine; However, sometimes the color returned by the RGB is not the color my mouse is on; Here is a pic ::
https://i.stack.imgur.com/R5qIx.png
Note: The white cross is an approximate location of where it is; A screenshot apparently does not take the mouse too.
The code is also laggy. Sometimes it hangs on one pixel and I have to wait until it runs again.
Why does this happen, and what can I do to prevent this? How can I solve this problem?
Since you only provided a code snippet, I created the following GUI.
Since the picture was so small, I put it under the RGB display.
The main problem was matching coordinate systems so that the mouse pointer would point to the pixel in the image. When you put the image inside a JLabel, you have to translate the mouse coordinate to an image coordinate.
I sidestepped the coordinate system problem by creating a drawing JPanel. By drawing the image on a drawing JPanel and adding the MouseMotionListener to the drawing JPanel, the mouse coordinates match the image coordinates.
I started the Swing application with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.
I created a JFrame, an RGB JPanel, and a drawing JPanel. The JFrame methods must be executed in a specific order. This is the order I use for all my Swing applications.
The RGB JPanel uses a FlowLayout to separate the red, green, and blue JTextFields.
The drawing JPanel draws the image. The drawing JPanel is the same size as the image. Again, by drawing the image on a drawing JPanel and adding the MouseMotionListener to the drawing JPanel, the mouse coordinates match the image coordinates.
I made the ColorListener a separate class. I basically copied your code to the mouseMoved method.
Here's the complete runnable code. I made the classes inner classes so I could paste this code as one block.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class RGBProcessor implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new RGBProcessor());
}
private BufferedImage image;
private Color panelColor;
private DrawingPanel drawingPanel;
private JPanel rgbPanel;
private JTextField redField;
private JTextField greenField;
private JTextField blueField;
public RGBProcessor() {
this.panelColor = Color.RED;
this.image = getImage();
this.drawingPanel = new DrawingPanel(this, image);
this.rgbPanel = createRGBPanel();
}
#Override
public void run() {
JFrame frame = new JFrame("RGB Processor");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(rgbPanel, BorderLayout.BEFORE_FIRST_LINE);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createRGBPanel() {
JPanel panel = new JPanel();
panel.setBackground(panelColor);
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
redField = new JTextField(4);
redField.setEditable(false);
panel.add(redField);
greenField = new JTextField(4);
greenField.setEditable(false);
panel.add(greenField);
blueField = new JTextField(4);
blueField.setEditable(false);
panel.add(blueField);
return panel;
}
private BufferedImage getImage() {
try {
return ImageIO.read(new URL("https://encrypted-tbn0.gstatic.com/"
+ "images?q=tbn:ANd9GcSFAhy8GM1F3_TyPjG7ezbe"
+ "FRUy00PN4hpJCA&usqp=CAU"));
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public void setPanelColor(Color color) {
this.panelColor = color;
this.rgbPanel.setBackground(color);
}
public JTextField getRedField() {
return redField;
}
public JTextField getGreenField() {
return greenField;
}
public JTextField getBlueField() {
return blueField;
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private BufferedImage image;
public DrawingPanel(RGBProcessor frame, BufferedImage image) {
this.image = image;
this.setPreferredSize(new Dimension(image.getWidth(),
image.getHeight()));
this.addMouseMotionListener(new ColorListener(frame, image));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
}
public class ColorListener extends MouseAdapter {
private RGBProcessor frame;
private BufferedImage image;
public ColorListener(RGBProcessor frame, BufferedImage image) {
this.frame = frame;
this.image = image;
}
#Override
public void mouseMoved(MouseEvent event) {
int packedInt = image.getRGB(event.getX(), event.getY());
Color color = new Color(packedInt, true);
frame.setPanelColor(color);
frame.getRedField().setText(Integer.toString(color.getRed()));
frame.getGreenField().setText(Integer.toString(color.getGreen()));
frame.getBlueField().setText(Integer.toString(color.getBlue()));
}
}
}

how to increase the size of the icons in the GUI and the size of the text using Java [duplicate]

I'd like to make a Java panel that creates objects where the user clicks. Since my actual application uses a MVC approach I'd like also for these objects to be able to repaint themselves when a model is changed, and provide menus to change their properties.
I think that the best way to control their x and y locations would be to take a canvas based approach whereby the JPanel calls a draw method on these objects from the paintComponent method. This however will only draw the shape on the canvas and does not add the object itself loosing all abilities to control object properties. I'd be very grateful if someone could tell me the best approach for what I want to do.
I've created some sample code which can be seen below. When clicked I'd like the circle to change colour, which is implemented using a MouseListener (it basically represents changing the models properties in this small example). Also I'd just like to make sure that zooming in/out still works with any sample code/advice can provide so I've added buttons to zoom the objects in and out as a quick test.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import java.awt.geom.Ellipse2D;
public class Main {
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ExamplePanel panel = new ExamplePanel();
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
});
}
//I could not get this to with when it extended JLayeredPane
private static class ExamplePanel extends JPanel {
private static final int maxX = 500;
private static final int maxY = 500;
private static double zoom = 1;
private static final Circle circle = new Circle(100, 100);
public ExamplePanel() {
this.setPreferredSize(new Dimension(maxX, maxY));
this.setFocusable(true);
Button zoomIn = new Button("Zoom In");
zoomIn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
zoom += 0.1;
repaint();
}
});
add(zoomIn);
Button zoomOut = new Button("Zoom Out");
zoomOut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
zoom -= 0.1;
repaint();
}
});
add(zoomOut);
// add(circle); // Comment back in if using JLayeredPane
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.scale(zoom, zoom);
super.paintComponent(g);
circle.paint(g); // Comment out if using JLayeredPane
}
}
static class Circle extends JPanel {
private Color color = Color.RED;
private final int x;
private final int y;
private static final int DIMENSION = 100;
public Circle(int x, int y) {
// setBounds(x, y, DIMENSION, DIMENSION);
this.x = x;
this.y = y;
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
color = Color.BLUE;
}
#Override
public void mouseReleased(MouseEvent e) {
}
});
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(color);
g2.fillOval(x, y, DIMENSION, DIMENSION);
}
// I had some trouble getting this to work with JLayeredPane even when setting the bounds
// In the constructor
// #Override
// public void paintComponent(Graphics g) {
// Graphics2D g2 = (Graphics2D) g;
// g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// g2.setPaint(color);
// g2.fillOval(x, y, DIMENSION, DIMENSION);
// }
#Override
public Dimension getPreferredSize(){
return new Dimension(DIMENSION, DIMENSION);
}
}
}
As an aside I did try using a JLayeredPane(useful because I'd also like to layer my objects) but could not get my objects to even render. I know it has no default layout manager so tried calling setBounds in the circle in the constructor, but sadly it did not work. I know it's better to use a layout manager but can't seem to find one suitable for my needs!
Thanks in advance.
Don't override paint components, use paintComponent and don't forget to call super.paintComponent
A component already has a concept of "location", so when painting, the top left position of your component is actually 0x0
What you are doing is actually painting beyond the boundaries of you component
For example, if you place your Circle at 100x100 and then did...
g2.fillOval(x, y, DIMENSION, DIMENSION);
You would actually start painting at 200x200 (100 for the actual location of the component and 100 for you additional positioning).
Instead use
g2.fillOval(x, y, DIMENSION, DIMENSION);
And go back and try using JLayeredPane.
You could actually write your own layout manager that takes the location of the component and it's preferred size and updates the components bounds and then apply this to a JLayeredPane. This gives you the "benefits" of an absolute layout, but keeps you within how Swing works to update its components when things change.
You should also be careful with doing anything like...
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
The Graphics context is a shared resource. That means, anything you apply to, will still be in effect when the next component is painted. This may produce some strange results.
Instead try using...
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//...
g2.dispose();
Updated
For zooming I would take a closer look at JXLayer (or JLayer in Java 7)
The JXLayer (and excellent PBar extensions) have gone quite on the net, so you can grab a copy from here
(I tried finding a better example, but this is the best I could do with the limited time I have available)
Updated with working zooming example
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.jdesktop.jxlayer.JXLayer;
import org.pbjar.jxlayer.demo.TransformUtils;
import org.pbjar.jxlayer.plaf.ext.transform.DefaultTransformModel;
public class TestJLayerZoom {
public static void main(String[] args) {
new TestJLayerZoom();
}
public TestJLayerZoom() {
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.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JXLayer<JComponent> layer;
private DefaultTransformModel transformModel;
private JPanel content;
public TestPane() {
content = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = 0;
JLabel label = new JLabel("Hello");
JTextField field = new JTextField("World", 20);
content.add(label, gbc);
content.add(field, gbc);
gbc.gridy++;
gbc.gridwidth = GridBagConstraints.REMAINDER;
final JSlider slider = new JSlider(50, 200);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
int value = slider.getValue();
double scale = value / 100d;
transformModel.setScale(scale);
}
});
content.add(slider, gbc);
transformModel = new DefaultTransformModel();
transformModel.setScaleToPreferredSize(true);
Map<RenderingHints.Key, Object> hints = new HashMap<>();
//hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
//hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
layer = TransformUtils.createTransformJXLayer(content, transformModel, hints);
setLayout(new BorderLayout());
add(layer);
}
}
}
I've left the rendering hints in to demonstrate their use, but I found that they screwed with the positing of the cursor within the text field, but you might like to have a play
I'd just like to add that I fixed the zooming issue not in the way suggested by the answer, but just by keeping the line that applied a scaled transform call in the ExamplePanel paintComponent method:
g2.scale(zoom, zoom);
I thought that this was the nicest implementation since none of the components require any knowledge about zooming and it seemed far simpler than JLayer since I only required basic zooming functionalities.

JPanel Animated Background

This is a really general question, but how should I add an animated background for a JPanel. I want the background to be behind all the panel's components and graphics. Right now, I have two separate classes (one for the main panel and the other for the background). The background class uses repaint() to animate a grid moving across the screen. I've tried to make the main panel background transparent, but that hasn't gotten me anywhere.
Even more info:
My main panel is part of a CardLayout and it has many different classes in it. So when I'm adding my main panel to my main frame, I'm doing frame.getContentPane().add(cards, BorderLayout.CENTER)
cards is a JPanel which acts as a container for the mainpanel and all the panels inside main panel.
Can anybody help me out in getting a panel animated background?
You can use Toolkit.getImage() to load animated image and then draw the image in container's paintComponent. Make sure the ImageObserver is set (not null) in order to update animation frames properly. For details how image is loaded, observed and updated see How Images are Loaded appendix in Java AWT Reference.
Here is a simple example:
import java.awt.*;
import javax.swing.*;
import java.net.URL;
class AnimatedPanelDemo {
static class ImagePanel extends JPanel {
private Image image;
ImagePanel(Image image) {
this.image = image;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image,0,0,getWidth(),getHeight(),this);
}
}
private static void createAndShowUI() {
try {
JFrame frame = new JFrame("Image");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
Image image = Toolkit.getDefaultToolkit().getImage(new URL(
"http://duke.kenai.com/iconSized/duke.running.gif"));
ImagePanel imagePanel = new ImagePanel(image);
imagePanel.add(new JLabel("Some label"));
frame.add(imagePanel);
frame.setSize(100, 100);
frame.setVisible(true);
}
catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
Well, this is my first answer on stackoverflow.
Will try to help with my learning curve with this complex AWT and Swift API.
Below there's the contructor that extends JFrame
package xpto;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.event.WindowStateListener;
import java.awt.image.ImageObserver;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import sun.java2d.SunGraphicsEnvironment;
import java.awt.Graphics;
import java.awt.Graphics2D;
public class FrameLuckyRaffle extends JFrame {
/**
*
*/
private JLabel backgroundLabel;
private ImageIcon imageIcon;
private Image bgImage;
/**
* Constructor of this frame.
*/
public FrameLuckyRaffle(String background, final String dbname) {
try {
setTitle("Lucky Raffle of "+ dbname);
GraphicsConfiguration config = this.getGraphicsConfiguration();
Rectangle usableBounds = SunGraphicsEnvironment.
getUsableBounds(config.getDevice());
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setBounds(100, 100, (int)(usableBounds.getWidth()*0.8),
(int)(usableBounds.getHeight()*0.8));
setMinimumSize(new Dimension(1024, 700));
setResizable(true);
setDefaultLookAndFeelDecorated(true);
backgroundLabel = new JLabel() {
public void paintComponent(Graphics g) {
// alternative --> g.drawImage(bgImage, 0, 0, null);
// I prefer to control the new ImageObserver parameter as bellow
g.drawImage(bgImage, 0, 0, new ImageObserver() {
#Override
public boolean imageUpdate(Image img, int infoflags,
int x, int y, int width, int height) {
img.getScaledInstance(getWidth(),getHeight(),
Image.SCALE_FAST);
return true;
}
});
// this is used to have easier control on
// image manipulation on my application
Graphics2D g2d = (Graphics2D)g;
super.paintComponent(g2d);
revalidate();
repaint();
}
};
backgroundLabel.setBounds(0, 0, 0, 0);
// this is necessary if you want more child
// components to be visible on the JFrame afterwards
backgroundLabel.setOpaque(false);
setContentPane(backgroundLabel);
addWindowListener(new WindowListener() {
#Override
public void windowOpened(WindowEvent e) {
// Set Frame Background
imageIcon = new ImageIcon(Toolkit.getDefaultToolkit().
createImage(FrameBusinessPure.class.getResource(background)));
bgImage = imageIcon.getImage().
getScaledInstance(getWidth(),getHeight(), Image.SCALE_FAST);
}
// Even after closing the window, JVM didn't Garbage Collected the instanced
// objects, for some reason. Forcing the objects to null helped on that.
#Override
public void windowClosed(WindowEvent e) {
backgroundLabel = null;
imageIcon = null;
bgImage = null;
System.gc();
}
});
addWindowStateListener(new WindowStateListener() {
#Override
public void windowStateChanged(WindowEvent e) {
// if you flush the object on runtime you will surpass the
// memory leak on using GIFs and most complex graphics
bgImage.flush();
bgImage = imageIcon.getImage().
getScaledInstance(getWidth(),getHeight(), Image.SCALE_FAST);
}
});
addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent e) {
// if you flush the object on runtime you will surpass the
// memory leak on using GIFs and most complex graphics
bgImage.flush();
bgImage = imageIcon.getImage().
getScaledInstance(getWidth(),getHeight(), Image.SCALE_FAST);
});
}catch (Exception e) {
e.printStackTrace();
}
}
}
Feel free to learn more on below link
https://www.oracle.com/java/technologies/painting.html

Using components in a JApplet that has a continually repainted JPanel

I'm having a major problem with this school assignment; lucky I started it early for once. We've been asked to make a children's math game using a JApplet. So far so good. I have managed to create a JPanel, which is then added to the JApplet and holds all the drawings (the JPanel contents are continually being redrawn). However, whenever I try to add a Swing component such as a JLabel to the JApplet content pane, it does not show or show signs of ever existing. I am completely new to JApplets so please don't be too harsh if it's obvious.
Below is the code:
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JApplet;
import javax.swing.JPanel;
import javax.swing.Timer;
public class CountingSheep extends JApplet
{
final int BOARDWIDTH = 800;
final int BOARDHEIGHT = 500;
final int SCREENWIDTH = 800;
final int SCREENHEIGHT = 800;
Dimension boardDim = new Dimension(BOARDWIDTH, BOARDHEIGHT);
Dimension screenDim = new Dimension(SCREENWIDTH, SCREENHEIGHT);
Graphics bufferGraphics;
Image offScreen;
Image backgroundImage;
Image[] sheepImage = new Image[2];
JPanel gameBoard = new JPanel(true);
List<Sheep> sheepArray = new ArrayList<>();
Timer myTimer;
public void init()
{
loadImages();
initScreen();
initBufferGraphics();
initBoard();
initTimer();
sheepArray.add(new Sheep(sheepImage));
myTimer.start();
}
private void loadImages()
{
sheepImage[0] = getImage(getDocumentBase(), "sheep.png");
sheepImage[1] = getImage(getDocumentBase(), "sheep2.png");
backgroundImage = getImage(getDocumentBase(), "bg.jpg");
}
private void initScreen()
{
setSize(800, 600);
setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
}
private void initBoard()
{
gameBoard.setPreferredSize(new Dimension(BOARDWIDTH, BOARDHEIGHT));
getContentPane().add(gameBoard);
}
private void initBufferGraphics()
{
offScreen = createImage(BOARDWIDTH, BOARDHEIGHT);
bufferGraphics = offScreen.getGraphics();
}
private void initTimer()
{
myTimer = new Timer(80, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
timerTick(e);
}
});
}
private void timerTick(ActionEvent e)
{
repaint();
}
public void paint(Graphics g)
{
bufferGraphics.clearRect(0, 0, BOARDWIDTH, BOARDHEIGHT);
bufferGraphics.drawImage(backgroundImage, 0, 0, null);
drawSheepHerd();
moveSheepHerd();
gameBoard.getGraphics().drawImage(offScreen, 0, 0, this);
}
public void drawSheepHerd()
{
for (Sheep s : sheepArray)
{
s.draw(bufferGraphics);
}
}
public void moveSheepHerd()
{
for (Sheep s : sheepArray)
{
s.move();
}
}
}
Thanks in advance, hope you guys can figure it out because I'm stumped.
To summarize some of my recommendations:
Create your own ContentPane class that extends JPanel, that overrides paintComponent(...) and that draws your background image and shows the animation.
Call setContentPane(...) on the JApplet in the init method, passing in an object of this class.
Experiment with different layouts and positionings for the ContentPane.
Make sure that the very first line of the paintComponent(Graphics g) method is: super.paintComponent(g) so that your drawing will be reset each time it paints.
JPanels are opaque by default, and you should leave it as such since contentPanes must be opaque. If you add components on top of the contentPane and want to see the image behind the added components, you may have to make them non-opaque.

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