I am currently trying to create a Java GUI program that generates images based on the arguments given in the terminal. If I get the argument in the command line java Draw Image1 for example, I want to draw my image 1 and etc. for the others. How can one take a command argument and use it in paintComponent? Here's a sample of what I am trying to do below:
import javax.swing.*;
import java.awt.*;
public class Draw extends JPanel
{
public Draw()
{
this.setSize(800,800);
JPanel drawing = new JPanel();
this.add(drawing);
this.setVisible(true);
}
protected void paintComponent(Graphics g)
{
if (args[0].equals("Image1")) // won't work
{
super.paintComponent(g);
Image myImage = Toolkit.getDefaultToolkit().getImage("image/myimage.jpg");
g.drawImage(myImage, 0, 0, this);
}
else
{
// draw image 2
}
}
public static void main(String[] args)
{
// create new Jframe
JFrame frame = new JFrame("Draw");
frame.add(new Draw());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500,500);
frame.setVisible(true);
}
}
Change this:
frame.add(new Draw());
to this:
frame.add(new Draw(args));
And then have your constructor accept a String array parameter and use it to set a class field.
public class Draw extends JPanel
{
private String[] params
public Draw(String params)
{
this.params = params;
this.setSize(800,800); // this generally should be avoided
JPanel drawing = new JPanel();
this.add(drawing);
this.setVisible(true);
}
protected void paintComponent(Graphics g)
{
super.paintComponent(g); // this should be out of the if block
if (params != null && params.length > 0 && params[0].equals("Image1")) {
// ..... etc
Edit: Andrew is right and I did not read your code carefully. Read your image in the constructor and use it in the image in the paintCompnent method
e.g.,
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
#SuppressWarnings("serial")
public class Draw extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = PREF_W;
private BufferedImage image;
public Draw(String[] params) throws IOException {
if (params != null && params.length > 0) {
image = ImageIO.read(new File(params[0]));
}
// this.setSize(800,800);
JPanel drawing = new JPanel();
drawing.setOpaque(false); // may need this
this.add(drawing);
this.setVisible(true);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
g.drawImage(image, 0, 0, null);
} else {
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
// not sure if you want to size it to the image or not
if (image != null) {
int w = image.getWidth();
int h = image.getHeight();
return new Dimension(w, h);
} else {
return new Dimension(PREF_W, PREF_H);
}
}
public static void main(String[] args) {
// create new Jframe
JFrame frame = new JFrame("Draw");
try {
frame.add(new Draw(args));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// frame.setSize(500,500);
frame.pack();
frame.setVisible(true);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Related
I'm experimenting with 2D painting in Swing, and I'd like to paint a JLabel in the middle of an empty JList.
Thus I came up with:
public class MyJList<T> extends JList<T> {
private final JLabel emptyLabel = new JLabel("Whatever");
#Override
public void paint(final Graphics g) {
super.paint(g);
if (getModel().getSize() == 0 && !emptyLabel.getText().isBlank()) {
final var preferredSize = emptyLabel.getPreferredSize();
final var x = (getWidth() - preferredSize.width) / 2;
final var y = (getHeight() - preferredSize.height) / 2;
final var g2 = (Graphics2D) g.create(x, y, preferredSize.width, preferredSize.height);
try {
emptyLabel.setBounds(0, 0, preferredSize.width, preferredSize.height);
emptyLabel.paint(g2);
} finally {
g2.dispose();
}
}
}
}
However when the text reaches the bounds of the list, it seems to get truncated:
If I increase the clipping rectangle width, the label is fully painted.
What am I doing wrong? Is this a good way to paint?
Complete minimal example:
class Main {
public static void main(final String[] args) {
final var myFrame = new MyFrame();
myFrame.setVisible(true);
}
public static class MyFrame extends JFrame {
public MyFrame() {
super("Example");
final var list = new MyJList<String>();
final var contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(list, BorderLayout.CENTER);
pack();
setMinimumSize(new Dimension(160, 200));
setLocationRelativeTo(null);
}
}
public static class MyJList<T> extends JList<T> {
private final JLabel emptyLabel = new JLabel("Selezionare un oggetto");
{
emptyLabel.setEnabled(false);
}
#Override
public void paint(final Graphics g) {
super.paint(g);
if (getModel().getSize() == 0) {
final var preferredSize = emptyLabel.getPreferredSize();
final var listBounds = getBounds();
final var x = (listBounds.width - preferredSize.width) / 2;
final var y = (listBounds.height - preferredSize.height) / 2;
final var g2 = (Graphics2D) g.create(x, y, preferredSize.width, preferredSize.height);
try {
emptyLabel.setBounds(0, 0, preferredSize.width, preferredSize.height);
emptyLabel.paint(g2);
} finally {
g2.dispose();
}
}
}
}
}
Text layout is tricky at the best of times, and the more you can avoid it, the better (IMHO).
Personally, I avoid the custom paint route, but this is me, and try something a little more simpler.
With the use of a PropertyChangeListener, to monitor when the ListModel changes, and a custom ListDataListener, to monitor when the ListModel itself changes, you can create a similar result.
One addition here is the fact that I've wrapped the text in <html> tags, this will trigger the "word" wrapping.
import java.awt.BorderLayout;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private DefaultListModel<String> model = new DefaultListModel<String>();
public TestPane() {
setLayout(new BorderLayout());
JPanel panel = new JPanel(new GridBagLayout());
JButton add = new JButton("Add");
JButton remove = new JButton("Remove");
panel.add(add);
panel.add(remove);
MyList<String> myList = new MyList<>();
myList.setModel(model);
add(new JScrollPane(myList));
add(panel, BorderLayout.SOUTH);
add.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
model.addElement("Hello");
}
});
remove.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
if (model.size() > 0) {
model.remove(0);
}
}
});
}
}
public class MyList<T> extends JList<T> {
final private ModelHandler modelHandler = new ModelHandler();
final private JLabel emptyLabel = new JLabel("<html>Selezionare un oggetto</html>");
public MyList() {
emptyLabel.setHorizontalAlignment(JLabel.CENTER);
setLayout(new BorderLayout());
add(emptyLabel);
emptyLabel.setVisible(false);
addPropertyChangeListener("model", new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ((evt.getOldValue() instanceof ListModel)) {
ListModel model = (ListModel) evt.getOldValue();
model.removeListDataListener(modelHandler);
}
if ((evt.getNewValue() instanceof ListModel)) {
ListModel model = (ListModel) evt.getNewValue();
model.addListDataListener(modelHandler);
}
updateEmptyLabel();
}
});
}
protected void updateEmptyLabel() {
emptyLabel.setVisible(getModel().getSize() == 0);
}
protected class ModelHandler implements ListDataListener {
#Override
public void intervalAdded(ListDataEvent evt) {
updateEmptyLabel();
}
#Override
public void intervalRemoved(ListDataEvent evt) {
updateEmptyLabel();
}
#Override
public void contentsChanged(ListDataEvent evt) {
updateEmptyLabel();
}
}
}
}
Calculating text size is difficult and has lots of gotchas. There is a reason why JList, JTable etc. make use of a renderer pane and not call paint on the cell renderer directly.
It is only when a component is part of a frame hierarchy it can correctly determine the size of text. That is because only then the component uses the correct GraphicsEnvironment for the current display device. The GraphicsEnvironment contains information that is necessary for laying out text as antialiasing support, kerning, fractional font support etc.
So the correct way do what you are doing is to add the label to the JList :
public static class MyJList<T> extends JList<T> {
private final JLabel emptyLabel = new JLabel("Selezionare un oggetto");
{
add(emptyLabel); // <---- add label
emptyLabel.setEnabled(false);
}
#Override
public void paint(final Graphics g) {
super.paint(g);
if (getModel().getSize() == 0) {
final var preferredSize = emptyLabel.getPreferredSize();
final var listBounds = getBounds();
final var x = (listBounds.width - preferredSize.width) / 2;
final var y = (listBounds.height - preferredSize.height) / 2;
final var g2 = (Graphics2D) g.create(x, y, preferredSize.width, preferredSize.height);
try {
emptyLabel.setBounds(0, 0, preferredSize.width, preferredSize.height);
emptyLabel.paint(g2);
} finally {
g2.dispose();
}
}
}
}
I am having trouble with Swing and the JScrollPane.
I am having a strange behaviour.
I extended JScrollPane. I display an image in it and draw rectangles over it to define areas.
With a big image, I have an Horizontal and a Vertical scrollbars.
I - ok - When I move one scrollbar or the other I see my image move too as it should.
II - not ok - When I move one scrollbar an leave it in between max and min position, then when I move my second scrollbar my image disappears.
With some debug prints, I found out that paintComponent, is not called when in case II.
I would like to know why it is not calling paintComponent and how I can fix it.
Here below is my class:
package GUI;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import javax.swing.JScrollPane;
public class DrawingPanel extends JScrollPane {
private static final long serialVersionUID = 1L;
private static final Color DRAWING_COLOR = new Color(255, 100, 200);
private static final Color FINAL_DRAWING_COLOR = Color.red;
private static final double ZOOMING_STEP = 1.1;
private Image sImg;
private Point startPt;
private Point endPt;
private Point currentPt;
private int prefW;
private int prefH;
private double zoomFactor = 1;
private boolean zoomer = false;
private boolean loaded = false;
public DrawingPanel() {
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void loadImage(Image img) {
sImg = img;
prefW = sImg.getWidth(null);
prefH = sImg.getHeight(null);
zoomFactor = getSize().getWidth() / prefW;
zoomer = true;
loaded = true;
repaint();
revalidate();
}
int countPaint = 0;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("paintComponent " + countPaint);
if (loaded) {
int zoomWidth = (int) (prefW * zoomFactor);
int zoomHeight = (int) (prefH * zoomFactor);
if (zoomer) {
((Graphics2D) g).scale(zoomFactor, zoomFactor);
setSize(zoomWidth, zoomHeight);
zoomer = false;
}
g.drawImage(sImg, 0, 0, zoomWidth, zoomHeight, null);
drawRectangle(g);
}
g.dispose();
countPaint++;
}
#Override
public Dimension getPreferredSize() {
return loaded ?
this.getSize() :
new Dimension((int) (prefW*zoomFactor), (int) (prefH*zoomFactor));
}
private void drawRectangle(Graphics g) {
Point secondPoint = (currentPt != null) ? currentPt : endPt;
Color color = (currentPt != null) ? DRAWING_COLOR : FINAL_DRAWING_COLOR;
if (startPt!=null && secondPoint!=null) {
int x = Math.min(startPt.x, secondPoint.x);
int y = Math.min(startPt.y, secondPoint.y);
int rectangleWidth = Math.abs(startPt.x - secondPoint.x);
int rectangleHeight = Math.abs(startPt.y - secondPoint.y);
g.setColor(color);
g.drawRect(x, y, rectangleWidth, rectangleHeight);
}
}
public void deleteRectangle(){
startPt = null;
endPt = null;
}
public void increaseZoom(Point p) {
double oldZoom = zoomFactor;
zoomFactor *= ZOOMING_STEP;
repositonPointAfterZoom(oldZoom, zoomFactor);
}
public void decreaseZoom(Point p) {
double oldZoom = zoomFactor;
zoomFactor /= ZOOMING_STEP;
repositonPointAfterZoom(oldZoom, zoomFactor);
}
public void repositonPointAfterZoom(double oldZoom, double newZoom) {
double evolution = newZoom/oldZoom;
if (startPt!=null) {
startPt.setLocation(startPt.x * evolution, startPt.y * evolution);
}
if (endPt!=null) {
endPt.setLocation(endPt.x * evolution, endPt.y * evolution);
}
repaint();
}
// Getter et setter
public void setStartPt(Point startPt) {
this.startPt = startPt;
}
public void setEndPt(Point endPt) {
this.endPt = endPt;
}
public void setCurrentPt(Point currentPt) {
this.currentPt = currentPt;
}
public int getZoomCalculateX(int value){
return (int) (value / zoomFactor);
}
public int getZoomCalculateY(int value){
return (int) (value / zoomFactor);
}
public void setZoomer(boolean zoomer) {
this.zoomer = zoomer;
}
}
EDIT : Bellow is the class (simplified) that uses DrawingPanel so you can have a reproducible exemple.
package GUI;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.apache.commons.io.FilenameUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import fileHandler.*;
public class GUI {
private JFrame frame;
private MenuBar menubar;
private DrawingPanel panelImage;
private JScrollPane scroll;
private GroundTruth openFile;
private int[] panelImageDown = new int[2];
private int[] panelImageUp = new int[2];
private Menu CoordinateMenu1 = new Menu();
private Menu CoordinateMenu2 = new Menu();
private int actualPagePdf;
private PDFRenderer renderer;
public static void main(String[] args) throws IOException {
new GUI();
}
public GUI() throws IOException {
JFrame frame = CreateFrame();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JFrame CreateFrame() throws IOException {
frame = new JFrame();
frame.setMenuBar(CreateMenuBar());
frame.setContentPane(SplitScreen());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("GTA - Ground Truth Annotator");
frame.setExtendedState(frame.getExtendedState() | JFrame.MAXIMIZED_BOTH);
return frame;
}
private MenuBar CreateMenuBar() {
menubar = new MenuBar();
menubar.add(CreateFileMenu());
menubar.add(new Menu("Selection coordinates:"));
menubar.add(CoordinateMenu1);
menubar.add(new Menu("Width/Height:"));
menubar.add(CoordinateMenu2);
return menubar;
}
private Menu CreateFileMenu() {
Menu mFile = new Menu("File");
MenuItem miOpenImage = new MenuItem("Open Image/PDF File");
mFile.add(miOpenImage);
miOpenImage.addActionListener(OpenFileActionListener);
mFile.addSeparator();
MenuItem miExit = new MenuItem("Exit Program");
mFile.add(miExit);
miExit.addActionListener(ExitActionListener);
return mFile;
}
private JPanel SplitScreen() throws IOException {
JPanel splittedScreen = new JPanel(new GridLayout(1, 2));
splittedScreen.add(CreateLeftPanel());
splittedScreen.add(CreateRightPanel());
return splittedScreen;
}
private JLayeredPane CreateLeftPanel() throws IOException {
JLayeredPane panel = new JLayeredPane();
panel.setLayout(new BorderLayout());
panel.setBorder(BorderFactory.createLineBorder(Color.gray));
panel.add(CreateImageScrollPane());
return panel;
}
private JScrollPane CreateImageScrollPane() throws IOException {
scroll = new JScrollPane(CreateImagePanel((String) null));
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scroll.getViewport().setScrollMode(JViewport.BLIT_SCROLL_MODE);
return scroll;
}
private DrawingPanel CreateImagePanel(String path) throws IOException {
if (panelImage == null) {
panelImage = new DrawingPanel();
}
if (path != null) {
panelImage.loadImage(ImageIO.read(new File(path)));
}
panelImage.addMouseListener(PanelImageMouseListener);
panelImage.addMouseWheelListener(PanelImageMouseWheelListener);
panelImage.addMouseMotionListener(PanelImageMouseMotionAdapter);
panelImage.setOpaque(false);
panelImage.revalidate();
panelImage.repaint();
panelImage.requestFocus();
return panelImage;
}
private DrawingPanel CreateImagePanel(Image image) throws IOException {
if (panelImage == null) {
panelImage = new DrawingPanel();
}
panelImage.loadImage(image);
panelImage.addMouseListener(PanelImageMouseListener);
panelImage.addMouseWheelListener(PanelImageMouseWheelListener);
panelImage.addMouseMotionListener(PanelImageMouseMotionAdapter);
panelImage.setOpaque(false);
panelImage.revalidate();
panelImage.repaint();
return panelImage;
}
private JPanel CreateRightPanel() {
JPanel panel = new JPanel(new GridLayout(0, 1));
//...
return panel;
}
ActionListener OpenFileActionListener = new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
OpenFile();
}
};
ActionListener ExitActionListener = new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Object[] options = {"Yes, quit now", "No, go back"};
int n = JOptionPane.showOptionDialog(
frame, "ATTENTION: closing without saving will cause any unsaved files to be lost. Do you want to proceed?",
"Warning", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]
);
switch (n) {
case JOptionPane.YES_OPTION:
System.exit(0);
case JOptionPane.NO_OPTION:
case JOptionPane.CANCEL_OPTION:
}
}
};
MouseListener PanelImageMouseListener = new MouseListener() {
public void mousePressed(MouseEvent me) {
panelImageDown = new int[]{
panelImage.getZoomCalculateX(me.getX()), panelImage.getZoomCalculateY(me.getY())
};
panelImageUp = null;
CoordinateMenu1.setLabel(String.format("%s:%s", panelImageDown[0], panelImageDown[1]));
CoordinateMenu2.setLabel("");
panelImage.setStartPt(me.getPoint());
panelImage.repaint();
}
public void mouseReleased(MouseEvent me) {
panelImageUp = new int[]{
Math.abs(panelImage.getZoomCalculateX(me.getX()) - panelImageDown[0]),
Math.abs(panelImageDown[1] - panelImage.getZoomCalculateY(me.getY()))
};
CoordinateMenu2.setLabel(String.format("%s:%s", panelImageUp[0], panelImageUp[1]));
panelImage.setEndPt(me.getPoint());
panelImage.setCurrentPt(null);
panelImage.repaint();
}
public void mouseClicked(MouseEvent arg0) {
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
};
MouseMotionAdapter PanelImageMouseMotionAdapter = new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent me) {
panelImageUp = new int[]{
Math.abs(panelImage.getZoomCalculateX(me.getX()) - panelImageDown[0]),
Math.abs(panelImageDown[1] - panelImage.getZoomCalculateY(me.getY()))
};
CoordinateMenu2.setLabel(String.format("%s:%s", panelImageUp[0], panelImageUp[1]));
panelImage.setCurrentPt(me.getPoint());
panelImage.repaint();
}
};
MouseWheelListener PanelImageMouseWheelListener = new MouseWheelListener() {
public void mouseWheelMoved(MouseWheelEvent me) {
if (me.isAltDown()) {
if (me.getWheelRotation() < 0) {
panelImage.setZoomer(true);
panelImage.increaseZoom();
panelImage.repaint();
panelImage.requestFocus();
//scroll.repaint();
//Zoom out
} else if(me.getWheelRotation() > 0) {
panelImage.setZoomer(true);
panelImage.decreaseZoom();
panelImage.repaint();
panelImage.requestFocus();
//scroll.repaint();
}
}
}
};
private void OpenFile() {
openFile = new GroundTruth();
JFileChooser fileChooser = new JFileChooser();
fileChooser.setCurrentDirectory(new File(System.getProperty("user.home")));
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooser.setAcceptAllFileFilterUsed(false);
fileChooser.addChoosableFileFilter(new FileNameExtensionFilter(
"Images / PDF Scan",
"bmp", "jpeg", "jpg", "png", "tif", "tiff", "pdf"
));
if (fileChooser.showOpenDialog(frame) != 0) {
return;
}
openFile.setFilename(fileChooser.getSelectedFile().getAbsolutePath());
if (getExtension(fileChooser.getSelectedFile().getName()).equals("pdf")) {
try {
PDDocument doc = PDDocument.load(fileChooser.getSelectedFile());
numberPagePdf = doc.getNumberOfPages();
actualPagePdf = 0;
renderer = new PDFRenderer(doc);
setPdfPage(actualPagePdf);
} catch (IOException e) {
e.printStackTrace();
}
} else {
try {
CreateImagePanel(fileChooser.getSelectedFile().getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void setPdfPage(int pageNumber){
try {
BufferedImage bim = renderer.renderImageWithDPI(pageNumber, 300);
refreshInfoPageSection();
CreateImagePanel(bim);
} catch (IOException e) {
e.printStackTrace();
}
}
// refresh the label who indicate the page display and set visible or inivisble the previous and next button
private void refreshInfoPageSection(){
panelImage.deleteRectangle();
}
public String getExtension(String filename) {
return FilenameUtils.getExtension(filename);
}
}
Here's a simplified example of a drawing JPanel that's larger than the scrolling JPanel.
You can see in the image that the drawing JPanel is larger both horizontally and vertically than the scrolling JPanel.
I don't call the paintComponent method at all in this code. Because I set up the GUI properly, Swing itself calls the repaint method when you move the scroll bars.
Here are the important things I did.
I started the Swing GUI with a call to the SwingUtilities invokeLater method. This method makes sure that the Swing components are created and executed on the Event Dispatch Thread.
I used a JFrame, two JPanels, and a JScrollPane. I extended JPanel to create the drawing panel. I used a JScrollPane, JPanel, and JFrame. The only time you extend a Swing component, or any Java class, is when you want to override one or more class methods.
I used Swing layout managers. I used a BorderLayout for the JFrame and scrolling JPanel.
Here's the complete runnable code. Why, you can even call it a minimal reproducible example!
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class LargeDrawingPanel implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new LargeDrawingPanel());
}
#Override
public void run() {
JFrame frame = new JFrame("Large Drawing Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createMainPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createMainPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
panel.setPreferredSize(new Dimension(400, 400));
JScrollPane scrollPane = new JScrollPane(new DrawingPanel());
panel.add(scrollPane, BorderLayout.CENTER);
return panel;
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingPanel() {
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(2000, 2000));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
int x = 100;
int y = 100;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
g.fillRect(x, y, 100, 100);
x += 200;
}
y += 200;
x = 100;
}
}
}
}
Edited to add: The OP posted additional questions in a comment.
Thanks for the example but it doesn't show me how to print an image in
a JPanel.
Pretty simple. Read an image using the ImageIO class, save the image in an application model consisting of one or more plain Java getter / setter classes and use the Graphics drawImage method to draw the image.
Your preview has only one scrollbar moved not both - which is my
problem.
Did you actually run the code I provided? I can only move one scrollbar at a time. The drawing JPanel extends both horizontally and vertically.
And it doesn't explain why my example doesn't work.
Your example is riddled with errors. Start over, building a Swing application one Swing component at a time using sound principles. The Oracle tutorial, Creating a GUI With JFC/Swing, will show you the correct way to create a Swing application. You can skip the Netbeans section.
I checked this code for hours, but the rectangle is not showing, can anyone tell me why it is not showing?:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
int resx = 700,resy = 500;
frame.setSize(resx,resy);
frame.setLocationRelativeTo(null);
frame.setTitle("Game");
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
try {
frame.setContentPane(new JLabel(new ImageIcon(ImageIO.read(new File("C:\\Users\\ivans\\Pictures\\Cookies.png")))));
} catch (IOException e) {
}
frame.repaint();
frame.setLayout(new FlowLayout());
frame.add(new JPanel(){
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillRect(50,50,450,650);
}
}, BorderLayout.CENTER);
frame.repaint();
frame.setVisible(true);
}
}
Every time I try to activate the background, the rectangle is not shown, and every time I activate the rectangle, the background is not shown. Please help!
You're setting the JFrame's contentPane to a JLabel, a container that uses no layout, and so adding a component to it will not allow that component to be displayed unless you fully specify that component's size and position, i.e., its bounds. This is one reason I avoid using JLabels for contentPanes (also that it will not set its preferred size based on the components it holds) and instead in general prefer to do my drawing in a background JPanel's paintComponent method.
Side recommendations:
You've too much going on in the main method -- unless this program is not for anything other than demonstration purposes
You set the JFrame's original contentPane (a JPanel) to FlowLayout, but understand that this is meaningless once you change the contentPane.
Despite your assuming that the contentPane uses FlowLayout, you're trying to add the drawing JPanel into a BorderLayout position, something that doesn't make sense.
You have an empty catch block, something that almost never should be done.
Get your images as resources, not files.
Avoid using absolute file paths and prefer use of relative paths to resources.
Don't set sizes of things if you can avoid it.
Avoid so-called "magic numbers", e.g., g.fillRect(50,50,450,650); as this makes your code hard to debug and enhance.
For example, something like:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class ShowRectangle extends JPanel {
private static final int RECT_X = 50;
private static final int RECT_Y = RECT_X;
private static final int RECT_W = 200;
private static final int RECT_H = 200;
private static final String URL_SPEC = "https://duke.kenai.com/guitar/DukeAsKeith-daylightSmall.png";
private BufferedImage img;
public ShowRectangle(BufferedImage img) {
this.img = img;
}
// have same JPanel draw image and graphic element
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, this);
}
// avoid magic numbers
// g.fillRect(50,50,450,650);
g.fillRect(RECT_X, RECT_Y, RECT_W, RECT_H);
}
// Size the JPanel to the image size
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet() || img == null) {
return super.getPreferredSize();
}
return new Dimension(img.getWidth(), img.getHeight());
}
private static void createAndShowGui(BufferedImage image) {
ShowRectangle mainPanel = new ShowRectangle(image);
JFrame frame = new JFrame("ShowRectangle");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
try {
URL imageUrl = new URL(URL_SPEC);
BufferedImage img = ImageIO.read(imageUrl);
SwingUtilities.invokeLater(() -> createAndShowGui(img));
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
}
If you avoid using "magic" numbers for instance, it's easy to make the black rectangle draggable, since it is now be drawn by variable values, values that you can change inside of a MouseAdapter (MouseListener and MouseMotionListener combined). For example:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
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.*;
#SuppressWarnings("serial")
public class ShowRectangle extends JPanel {
private static final int RECT_X = 50;
private static final int RECT_Y = RECT_X;
private static final int RECT_W = 200;
private static final int RECT_H = 200;
private static final String URL_SPEC = "https://duke.kenai.com/guitar/DukeAsKeith-daylightSmall.png";
private int rectX = RECT_X;
private int rectY = RECT_Y;
private BufferedImage img;
public ShowRectangle(BufferedImage img) {
this.img = img;
MouseAdapter myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
// have same JPanel draw image and graphic element
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, this);
}
// avoid magic numbers
// g.fillRect(50,50,450,650);
g.fillRect(rectX, rectY, RECT_W, RECT_H);
}
// Size the JPanel to the image size
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet() || img == null) {
return super.getPreferredSize();
}
return new Dimension(img.getWidth(), img.getHeight());
}
private class MyMouse extends MouseAdapter {
private Point p1;
private Point rectP = null;
#Override
public void mousePressed(MouseEvent e) {
p1 = e.getPoint();
if (new Rectangle(rectX, rectY, RECT_W, RECT_H).contains(p1)) {
rectP = new Point(rectX, rectY);
}
}
#Override
public void mouseDragged(MouseEvent e) {
moveRect(e.getPoint());
}
#Override
public void mouseReleased(MouseEvent e) {
moveRect(e.getPoint());
rectP = null;
}
private void moveRect(Point p2) {
if (rectP == null) {
return;
}
rectX = rectP.x + p2.x - p1.x;
rectY = rectP.y + p2.y - p1.y;
repaint();
}
}
private static void createAndShowGui(BufferedImage image) {
ShowRectangle mainPanel = new ShowRectangle(image);
JFrame frame = new JFrame("ShowRectangle");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
try {
URL imageUrl = new URL(URL_SPEC);
BufferedImage img = ImageIO.read(imageUrl);
SwingUtilities.invokeLater(() -> createAndShowGui(img));
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
}
Its there just it was going out of boundary and hence not visible.
replace these two lines and check
g.setColor(Color.BLACK);
g.fillRect(0,0,250,250);
Try this,
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Rectangle clipBounds = g.getClipBounds();
System.out.println(clipBounds.getX() +" "+ clipBounds.getY() + " "+ clipBounds.getHeight() + " " + clipBounds.getWidth());
g.setColor(Color.BLACK);
g.fillRect(0,0,450,450);
}
You will get 0.0 0.0 10.0 10.0
as output meaning the fill reactangle container is starting from 0,0 till 10,10 and hence it was not shown
Change the layout to GridLayout() will solve your problem,
frame.setLayout(new GridLayout());
frame.add(new JPanel(new GridLayout()){
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Rectangle clipBounds = g.getClipBounds();
System.out.println(clipBounds.getX() +" "+ clipBounds.getY() + " "+ clipBounds.getHeight() + " " + clipBounds.getWidth());
g.setColor(Color.BLACK);
g.fillRect(frame.getWidth()/2,0,frame.getWidth(),frame.getWidth());
}
}, BorderLayout.CENTER);
As per doc,
void java.awt.Graphics.fillRect(int x, int y, int width, int height)
Fills the specified rectangle. The left and right edges of the rectangle are at x and x + width - 1. The top and bottom edges are at y and y + height - 1. The resulting rectangle covers an area width pixels wide by height pixels tall. The rectangle is filled using the graphics context's current color.
Parameters:
x the x coordinate of the rectangle to be filled.
y the y coordinate of the rectangle to be filled.
width the width of the rectangle to be filled.
height the height of the rectangle to be filled.
So that's the issue
I am learning java gui interface and wrote a program that has a button. Each time the button is clicked, a random sized rectangle will be added to the screen. But instead of adding it to the screen, the program keeps erasing the old one, which I want to keep on the screen. Here is my code. I tried to do paint() and it did not work. Thanks in advance.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class SimpleGui implements ActionListener {
JFrame frame = new JFrame();
public static void main(String[] args){
SimpleGui gui = new SimpleGui();
gui.go();
}
public void go(){
JButton button = new JButton("Add a rectangle");
MyDrawPanel panel = new MyDrawPanel();
button.addActionListener(this);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.setSize(300, 300);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event){
frame.repaint();
}
class MyDrawPanel extends JPanel{
public void paintComponent(Graphics g){
g.setColor(Color.blue);
int height = (int) (Math.random()*120 + 10);
int width = (int) (Math.random()*120 + 10);
int x = (int) (Math.random()*40 + 10);
int y = (int) (Math.random()*40 + 10);
g.fillRect(x, y, height, width);
}
}
}
Your paintComponent method is written to draw only one rectangle, so its behavior should come as no shock to you. If you want it to draw multiple, you have one of two options:
Create an ArrayList<Rectangle>, and in the actionPerformed method, add a new random Rectangle to this List and then call repaint(). In the paintComponent method, iterate through this List with a for-loop, painting each Rectangle.
Or you could draw the new random rectangle onto a BufferedImage that is displayed by the paintComponent method.
The first method is the easier of the two, the 2nd is better if you're worried about program responsiveness, say in an animation program.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class TwoDrawRectMethods extends JPanel {
// Array to hold our two drawing JPanels
private AddRandomRect[] addRandomRects = {
new DrawList("Using List"),
new DrawBufferedImage("Using BufferedImage")};
// constructor
public TwoDrawRectMethods() {
// add drawing rectangles onto GUI
for (AddRandomRect addRandomRect : addRandomRects) {
add(addRandomRect);
}
// button to tell rectangles to add a new Rectangle
add(new JButton(new DrawAction("Add New Rectangle")));
}
// The button's Action -- an ActionListener on "steroids"
private class DrawAction extends AbstractAction {
public DrawAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
// tell both drawing JPanels to add a new rectangle
for (AddRandomRect addRandomRect : addRandomRects) {
addRandomRect.addRectangle();
}
}
}
private static void createAndShowGui() {
TwoDrawRectMethods mainPanel = new TwoDrawRectMethods();
JFrame frame = new JFrame("TwoDrawRectMethods");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
#SuppressWarnings("serial")
class DrawList extends AddRandomRect {
private static final Color RECT_COLOR = Color.RED;
private List<Rectangle> rectList = new ArrayList<>();
public DrawList(String title) {
super(title);
}
#Override
public void addRectangle() {
rectList.add(createRandomRect());
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(RECT_COLOR);
for (Rectangle rectangle : rectList) {
g2.draw(rectangle);
}
}
}
#SuppressWarnings("serial")
class DrawBufferedImage extends AddRandomRect {
private static final Color RECT_COLOR = Color.BLUE;
private BufferedImage img = null;
public DrawBufferedImage(String title) {
super(title);
}
#Override
public void addRectangle() {
if (img == null) {
img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
}
Rectangle rect = createRandomRect();
Graphics2D g2 = img.createGraphics();
g2.setColor(RECT_COLOR);
g2.draw(rect);
g2.dispose();
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, null);
}
}
}
#SuppressWarnings("serial")
abstract class AddRandomRect extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private Random random = new Random();
public AddRandomRect(String title) {
setBorder(BorderFactory.createTitledBorder(title));
}
abstract void addRectangle();
protected Rectangle createRandomRect() {
int x1 = random.nextInt(PREF_W);
int x2 = random.nextInt(PREF_W);
int y1 = random.nextInt(PREF_H);
int y2 = random.nextInt(PREF_H);
int x = Math.min(x1, x2);
int y = Math.min(y1, y2);
int width = Math.abs(x1 - x2);
int height = Math.abs(y1 - y2);
return new Rectangle(x, y, width, height);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.File;
public class spriteStore {
public static BufferedImage playerStanding;
public void getImage()
{
try
{
playerStanding = ImageIO.read(new File("Cobalt\\pictures\\playerStanding1.png"));
}
catch(Exception e){System.out.println("Picture not found");}
}
}
I am trying to read an image to save as a BufferedImage object, but when i run the main code,
import java.awt.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Cobalt {
public Boolean movingLeft, movingRight, firstJump, secondJump;
public int jump = 0;
public Dimension screenSize;
public JFrame frame;
public JPanel panel;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable()
{
public void run(){
new Cobalt();
}
});
}
public Cobalt()
{
frame = new JFrame("COBALT");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setResizable(true);
frame.setSize(500,500); //width and height
panel = new MyPanel();
frame.getContentPane().add(panel);
}
class MyPanel extends JPanel
{
private static final long serialVersionUID = 1L;
public void paint(Graphics g)
{
Graphics g2D = (Graphics2D) g;
super.paint(g);
g2D.drawImage(spriteStore.playerStanding, 100, 100, null);
}
}
}
and the image will not show up. I'm using eclipse, and am relatively a noob, so please inform me of my error.
There is no need to do custom painting to show an image.
You can use a JLabel. Read the section from the Swing tutorial on How to Use Icons.
I assume that you have in your project a structure like this:
Cobalt
|
\---src
| Cobalt.java
|
\---pictures
playerStanding1.png
Try the following:
public static void main(String[] args) throws Exception {
URL url = ClassLoader.getSystemClassLoader().
getResource("pictures/playerStanding1.png");
BufferedImage playerStanding = ImageIO.read(url);
JLabel label = new JLabel(new ImageIcon(playerStanding));
JOptionPane.showMessageDialog(null, label);
}
Try this to create the jlabel
ImageIcon icon = createImageIcon("images/middle.gif",
"a pretty but meaningless splat");
JLabel thumb = new JLabel();
thumb.setIcon(icon);
And load the image like this
/** Returns an ImageIcon, or null if the path was invalid. */
protected ImageIcon createImageIcon(String path,
String description) {
java.net.URL imgURL = getClass().getResource(path);
if (imgURL != null) {
return new ImageIcon(imgURL, description);
} else {
//System.err.println("Couldn't find file: " + path);
return null;
}
}
And if you want it from a url
URL PicURL = new URL("http://...");
ImageIcon imgThisImg = new ImageIcon(PicURL));
jLabel2.setIcon(imgThisImg);