Skip to main content
gamedev.net
🔒 Locked

[java] Java2D With An Application?

Started by Gallivan Dec 27, 2008 at 7:06 PM 17 replies 2.1k views
Original Post
Gallivan
Gallivan
I'm very confused regarding drawing simple 2D shapes with a JAVA application. In school we learned how to draw with an Applet, but now I am looking to draw with an application. Will I be using paint(Graphics g) or Swing? I've read http://java.sun.com/docs/books/tutorial/2d/overview/index.html and http://java.sun.com/docs/books/tutorial/2d/index.html but I get confused when I see the import JApplet and various commands. Are there any tutorials out there for applications?
MONDARIZ
MONDARIZ
I just had a quick browse through the tutorials and couldn't identify the part you mentioned.

Could you specify which part of the tutorials that is confusing you (maybe cut and paste the paragraph)?

Depending on what you are trying to do, its possible to use paint(g) to draw in your swing component.

(pseudo code)

class Swingwindow extends JFrame

public void paint(graphics g){

super.paint(g);
g.drawLine(0,0,50,50);
}

Java will display swing components without a draw/paint call, but if you need anything else it must be drawn or painted.
Gallivan
Gallivan
That actually was all I needed. I guess I was just under the assumption that 'paint' was for Applets only. Here's what I ended up with, thanks to your pseudo code:

import java.awt.*;import javax.swing.*;public class Swingwindow extends JFrame {    public static void main(String[] args) {        JFrame f = new JFrame("Weather Wizard");        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);              f.pack();        f.setVisible(true);            }    public void paint(Graphics g) {        Graphics2D g2 = (Graphics2D) g;        super.paint(g2);        g2.drawLine(0,0,50,50);    }}
MONDARIZ
MONDARIZ
I'm glad to hear you found your way...

Paint is a method from Component (or maybe its Container), so any class under that will have access to the paint method through inheritance. This includes JApplet, which is a sub-class of Component/Container.

Its a good idea to find some meaning in draw/paint/repaint/drawimage/paintcomponent/update and so on.

Here are a few links:

http://leepoint.net/notes-java/GUI-lowlevel/graphics/15who-calls-paintcomponent.html

http://java.sun.com/products/jfc/tsc/articles/painting/index.html
dmatter
dmatter
Quote:
Original post by Gallivan
That actually was all I needed. I guess I was just under the assumption that 'paint' was for Applets only.
If you want to do your own painting in Swing it's preferable not to override paint but instead to override paintComponent, for more information see the links above in MONDARIZ's post.
Gallivan
Gallivan
I'm having trouble adding a JLabel to a direct position on-screen.

The documentation reads:
add(javax.swing.JLabel jLab, int x, int y, int dx, int dy)Adds a JLabel at position x;y on the window and with a width/height of dx/dy.


But my code of:
frame.getContentPane().add(unitLabel, x, y, 16, 16);


Gives me an error of:

cannot find symbol - method add(javax.swing.JLabel,int,int,int,int)

This was my reference:

http://ltiwww.epfl.ch/sJava/version2/javadocDir/epfl/lti/Utilities/AFrame.html
swiftcoder
swiftcoder
Quote:
Original post by Gallivan
This was my reference: http://ltiwww.epfl.ch/sJava/version2/javadocDir/epfl/lti/Utilities/AFrame.html
Prefer the official java documentation: http://java.sun.com/javase/6/docs/api/
Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]
Gallivan
Gallivan
I've tried this:

unitLabel.setBounds(x, y, unitImage.getIconWidth(), unitImage.getIconHeight());

It compiles, but the image icon remains centered in my frame.
MONDARIZ
MONDARIZ
I think its because you add to the toplevel container (directly to the JFrame).
JFrame inherits the add() from Container and that add() does not accept the parameter (unitLabel, x, y, 16, 16).

It accepts these:

add(Component comp)
add(Component comp, int index)
add(Component comp, Object constraints)
add(Component comp, Object constraints, int index)
add(String name, Component comp)


You should look at the LayoutManagers, maybe the GridBagLayout can help you.

You can also post your entire code, then it might be easier to explain whats happening.
Gallivan
Gallivan
import java.awt.*;import javax.swing.*;public class Display extends JFrame {        public void main(String[] args) {        createGUI();        showUnit(0,0);        showUnit(0,0);    }        protected ImageIcon createImageIcon(String path) {        java.net.URL imgURL = getClass().getResource(path);        if (imgURL != null) {            return new ImageIcon(imgURL);        } else {            System.err.println("Couldn't find file: " + path);            return null;        }    }        private void createGUI() {            }        private void showUnit(int x, int y) {        JFrame frame = new JFrame("TopLevelDemo");        ImageIcon unitImage = createImageIcon("images/middle.gif");        JLabel unitLabel = new JLabel(unitImage);        unitLabel.setOpaque(true);        unitLabel.setBackground(new Color(248, 213, 131));        unitLabel.setPreferredSize(new Dimension(16, 16));        unitLabel.setBounds(x, y, unitImage.getIconWidth(), unitImage.getIconHeight());        frame.getContentPane().add(unitLabel);        frame.pack();        frame.setVisible(true);    }}
MONDARIZ
MONDARIZ
Does that code run for you?

I'm not sure what example you are working from, but here are a few things i would change. I have not made too many changes in the actual design of your class.

Primarily you need to declare the main method static:

public static void main(String[] args)


If this method is not static, you can't access it unless the class is instanciated. Since this method creates the Display class instance, its kind of a chicken and egg thing.

Secondly, as it looks now, you should create your Display object as a variable of the type Display. Then call the two methods in that object.

Display dis = new Display();        dis.createGUI();        dis.showUnit(0,0);


Since the createGUI method is empty, maybe you should remove it to make the code more clear.

In your showUnit method, you create a JFrame. If you think about it, you might see that the Display class already extends JFrame and thus is a JFrame. If you move the "JFrame name" from this call, to the call from main, you will still get a JFrame called "TopLevelDemo". To make this work, you will also need a constructor for the Display class.

public class Display extends JFrame {        public Display(String string) {			}	public static void main(String[] args) {    	    	Display dis = new Display("TopLevelDemo");        dis.createGUI();        dis.showUnit(0,0);            }


A final change to the code is the removal of a few things in the last three calls. You can add the JLabel directly to the Display JFrame. The same goes for pack and setVisible. Java "knows" that Display is a JFrame and will associate the calls with this JFrame.

private void showUnit(int x, int y) {        ImageIcon unitImage = createImageIcon("images/middle.gif");        JLabel unitLabel = new JLabel(unitImage);        unitLabel.setOpaque(true);        unitLabel.setBackground(new Color(248, 213, 131));        unitLabel.setPreferredSize(new Dimension(16, 16));        unitLabel.setBounds(x, y, unitImage.getIconWidth(), unitImage.getIconHeight());               add(unitLabel);        pack();        setVisible(true);    }


To make things a bit clear (in particular with complex classes), I would add this to those 3 calls, to indicate that they are related to this object (the Display JFrame).

this.add(unitLabel);        this.pack();        this.setVisible(true);



To reposition your JLabel, I suggest you use a LayoutManager. These are added to the (in this case) JFrame. Component are added differently that when drawing an image. If you were drawing the image directly to the JFrame, you could use the position as a parameter in the call.

g.drawImage(img, 100, 100, this);


Since you are using the image as a JLabel, you can devide the JFrame into different areas with a layoutmanager. To see the difference I have given your JFrame a size.

this.setPreferredSize(new Dimension(175, 100));        this.add(unitLabel, BorderLayout.NORTH);


Maybe you could give a description of what you expect the application to do, when you are surpriced the image is in the center of the frame.

[Edited by - MONDARIZ on December 30, 2008 5:23:18 AM]
MONDARIZ
MONDARIZ
I just realised that your "remains in the center" problem might be caused by the pack() call.

When the JFrame contains only one component, it will be packed around this component and the component will remain centered no matter where you place it.

Try to comment out the setPreferredSize line and use this:

this.setSize(200, 300);


You will find that the JFrame is unaltered, with the component in the center.

If you now comment out the pack() call, you will see that the JFrame is created with the dimensions from setSize.

[Edited by - MONDARIZ on December 30, 2008 5:38:56 AM]
MONDARIZ
MONDARIZ
To load and display an image, try using this code:

import java.awt.*;import java.awt.event.*;import java.awt.image.*;import java.io.*;import javax.imageio.*;import javax.swing.*;public class Viewer extends JPanel {              BufferedImage img;    public void paint(Graphics g) {        g.drawImage(img, 50, 50, null);    }    public Viewer() {       try {           img = ImageIO.read(new File("middle.jpg"));       } catch (IOException e) {       }    }    public Dimension getPreferredSize() {        if (img == null) {             return new Dimension(100,100);        } else {           return new Dimension(img.getWidth(null), img.getHeight(null));       }    }    public static void main(String[] args) {        JFrame f = new JFrame("Load Image Sample");                    f.addWindowListener(new WindowAdapter(){                public void windowClosing(WindowEvent e) {                    System.exit(0);                }            });        f.add(new Viewer());        f.setSize(200, 300);        f.setVisible(true);    }}


The image should be located in the application folder. By changing the int values in the drawImage call, you can change the location of the image on the panel.

You might see that the Viewer is a JPanel, which is added to the JFrame. Then the image is drawn on the JPanel. I think this is better than using the JFrame on its own.
Gallivan
Gallivan
Thanks for being incredibly helpful.

My main class was not static because at first I had an instance variable that was the JFrame which I later moved into a method.

I wasn't aware that you could draw an image to a component like that. What I was trying to do is get a basic image on the screen which could be moved by input (the arrow keys).

So my plan was to:

- get the input
- affect the image (xLoc, yLoc)
- draw the background
- draw the image

And repeat that until the program closed.

I understand most of the Viewer class you provided. Is paint always called after main()?

Could I make another method called 'public void drawBackground(Graphics g)' that the main method could call?

Again, thanks for the time you've put into this.
MONDARIZ
MONDARIZ
In this case paint() is about drawing the image on the JPanel, so it must naturally be after the JPanel has been created from main (and the image loaded by ImageIO()):

 f.add(new Viewer());


Your paint method should paint everything on the JPanel.

sort of like this:

 public void paint(Graphics g) {        g.drawImage(img, 50, 50, null);        g.drawImage(img, 100, 100, null);    }


This is the more primitive way of doing things, but it will work. The images are drawn in turn, so image 2 will be on top of image 1. Remember you can set an image to have transparency so the "unused" area wont be drawn.

if you need to move things around, its best to use sprites.
MONDARIZ
MONDARIZ
It might be helpful to know, that when you create an instance of JPanel (as with class Viewer extends JPanel), there is automatically a call to paint() when it is displayed on screen (otherwise you wouldn't see it at all).

Whats happening in the Viewer class, is that we override the default paint() and make it draw the images.

Strangely the drawImage method does not actually draw the image, but facilitate the loading of the image. An imageObserver is attached (in this case the parent component), which draw the image.

So the fourth parameter in imageDraw() should actually be this

g.drawImage(img, 50, 50, this);


However, the ImageIO read() might work as an ImageObserver (since we can use null in the example), I'm not sure.
Gallivan
Gallivan
Thanks, I wasn't aware that paint was called upon instantiation. I'll probably read through more of the methods in the Graphics class so I don't always have to look them up.
Gallivan
Gallivan
How would I create a method for a class which draws, but isn't auto-called like paint?

For example:

public class StickFigure {    drawStickFigure(Graphics g) {        g.drawImage(...etc);    }}


Then I would:

StickFigure mitch = new StickFigure();mitch.drawStickFigure();


But my problem is when I do mitch.drawStickFigure() it requires a graphical parameter. But I can't send Graphics g, because it's abstract...

I hope that was clear enough.
Gallivan
Gallivan
Figured it out, just going to use repaint().

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.