1

I'm starting to teach myself Java, and it's possible my reach doth exceed my grasp. Still, that's the best way to learn, right?

I'm trying to play around with simple graphics. I've found various resources on how to draw shapes into a JFrame, and made them work, but as far as I can tell the code always needs to go inside the paint method, which gets called automatically, which means I can't figure out how to go about passing it arguments.

What I'm trying to do is write some code that can take co-ordinates as arguments, and then place a simple shape (let's go with a 10x10 pixel square) at those co-ordinates. I feel sure that must be something that's possible, but I cannot for the life of me work out how.

Code so far, incorporating help from both @resueman and @Mikle:

public class Robots {

    public static void main(String[] args) {
        Window window = new Window();
        window.Window();
        new PlayArea();
        Point p = new Point(50,50);
        Entity player1 = new Entity();
        player1.Entity(p);
    }

}

public class Window extends JFrame{
    int x;
    int y;

    public void Window(){
        Window window = new Window();
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setTitle("Robots");
        window.setSize(800,600);
        window.getContentPane().add(new PlayArea());
        window.setLocationRelativeTo(null);
        window.setBackground(Color.white);
        window.setVisible(true);
    }    
}

public class PlayArea extends JComponent{
    // I keep all added and displayed entities here for easier access
    public final List<Entity> entities = new ArrayList<Entity> ();

    public PlayArea(){
        super();
    }

    public void addEntity(final Entity entity){
        //add new entity and repaint area
        entities.add(entity);
    }
    @Override
    protected void paintComponent (final Graphics g)
    {
        super.paintComponent (g);

        // Painting entities
        final Graphics2D g2d = (Graphics2D) g;
        for (final Entity entity : entities)
        {
            g2d.setPaint ( Color.BLACK );
            g2d.fill (getEntityShape (entity));
        }
    }

        protected Shape getEntityShape ( final Entity entity )
        {
            // Simple entity shape, you can replace it with any other shape
            return new Rectangle ( entity.entPos.x - 5, entity.entPos.y - 5, 10, 10 );
        }
}

public class Entity extends JComponent{
    protected Point entPos = null;

    public void Entity(Point p){
        entPos = p;
        repaint();
    }

    @Override
    public void paintComponent (Graphics g){
        super.paintComponent(g);
        if (entPos != null){
            g.fillRect(entPos.x, entPos.y, 10,10);
        }
    }
}

I want to be able to create an object in the Entity class, and put it in the window at the x,y co-ordinates.

I will eventually want to be able to move Entities around, but I'll work that one out once I've figured out how to draw them in the first place!

Still not drawing anything in the window. I'm probably missing something really obvious though.

Tam Coton
  • 786
  • 1
  • 9
  • 20
  • Did you check the [Java tutorials on 2d graphics](http://docs.oracle.com/javase/tutorial/2d/TOC.html)? – Keppil Jul 10 '14 at 13:05
  • I've looked at the Drawing Geometric Primitives and Drawing Arbitrary Shapes tutorials. As I mentioned, as far as I can tell everything has to go inside the paint method, and I can't see how to call it on demand. Is there something specific I overlooked in there? – Tam Coton Jul 10 '14 at 13:11
  • A related example is cited [here](http://stackoverflow.com/a/11944233/230513). – trashgod Jul 10 '14 at 15:26

2 Answers2

3

There are a lot of possible approaches to solve your task.
Here are the first two options which come to mind:

  1. Use a custom component and paint all Entity objects in it

    This one will be easy and fast to implement. Implementing entities drag also won't be a big issue. There are also a few options how painting can be done here. This approach is good in case you are going to have just a few simple elements painted on the area and don't want to make your code too complicated.

  2. Paint each Entity on a separate component, and place them using layout managers

    This is harder to achieve, but it will use much less resources and will be painted faster due to Swing repainting optimizations. This approach is much better if you are aiming at a large amount of elements or large area size. But that might be an overkill for your case.

Here is a simple example of the 1st approach (including entities drag):

public class SingleComponent extends JFrame
{
    public SingleComponent () throws HeadlessException
    {
        super ();
        setTitle ( "Robots" );
        setBackground ( Color.white );

        // Adding our custom component into the frame
        getContentPane ().add ( new EntitiesArea () );

        setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
        setSize ( 800, 600 );
        setLocationRelativeTo ( null );
        setVisible ( true );
    }

    public class Entity
    {
        int x;
        int y;

        public Entity ( final int x, final int y )
        {
            this.x = x;
            this.y = y;
        }
    }

    public class EntitiesArea extends JComponent
    {
        // I keep all added and displayed entities here for easier access
        public final List<Entity> entities = new ArrayList<Entity> ();

        public EntitiesArea ()
        {
            super ();

            // Adding mouse adapter that will add/drag entities
            final MouseAdapter mouseAdapter = new MouseAdapter ()
            {
                // Currently dragged entity
                private Entity dragged = null;

                @Override
                public void mousePressed ( final MouseEvent e )
                {
                    // Looking for entity under mouse
                    Entity underPoint = null;
                    for ( final Entity entity : entities )
                    {
                        if ( getEntityShape ( entity ).contains ( e.getPoint () ) )
                        {
                            underPoint = entity;
                            break;
                        }
                    }

                    // Either dragging existing entity or adding new one
                    if ( underPoint != null )
                    {
                        dragged = underPoint;
                    }
                    else
                    {
                        addEntity ( new Entity ( e.getX (), e.getY () ) );
                    }
                }

                @Override
                public void mouseDragged ( final MouseEvent e )
                {
                    if ( dragged != null )
                    {
                        // Change entity coordinate and repaint area
                        dragged.x = e.getX ();
                        dragged.y = e.getY ();
                        repaint ();
                    }
                }

                @Override
                public void mouseReleased ( final MouseEvent e )
                {
                    if ( dragged != null )
                    {
                        dragged = null;
                    }
                }
            };
            addMouseListener ( mouseAdapter );
            addMouseMotionListener ( mouseAdapter );
        }

        public void addEntity ( final Entity entity )
        {
            // Add new entity and repaint area
            entities.add ( entity );
            repaint ();
        }

        @Override
        protected void paintComponent ( final Graphics g )
        {
            super.paintComponent ( g );

            // Painting entities
            final Graphics2D g2d = ( Graphics2D ) g;
            for ( final Entity entity : entities )
            {
                g2d.setPaint ( Color.BLACK );
                g2d.fill ( getEntityShape ( entity ) );
            }
        }

        protected Shape getEntityShape ( final Entity entity )
        {
            // Simple entity shape, you can replace it with any other shape
            return new Rectangle ( entity.x - 20, entity.y - 20, 40, 40 );
        }
    }

    public static void main ( final String[] args )
    {
        new SingleComponent ();
    }
}

I didn't add any repaint optimizations to this example to make it as simple as it could be, but keep in mind that any action in this example repaints the whole area and forces each entity to be painted from a scratch. This won't even be noticeable until a large amount of elements appear on the area, but make sure you take care of optimized repaint(...) calls later as this is one of the things that make Swing so fast and so good.

Mikle Garin
  • 10,083
  • 37
  • 59
  • I'm sure that's very helpful, but it's beyond my current comprehension level! What's going on in that code? I don't want to use code I don't understand, that's a surefire way to not be able to debug when something goes wrong! – Tam Coton Jul 11 '14 at 10:30
  • There is nothing hard about this specific code example, ask what exactly you don't understand and I will properly explain it – Mikle Garin Jul 11 '14 at 12:46
  • Basically all I have added to your initial code example is an additional class `EntitiesArea` which has mouse adapter to handle entities add/drag and painting method that directly paints all entities added into the list. – Mikle Garin Jul 11 '14 at 12:49
  • I'm not certain what super() does by itself, and the line `public final List entities = new ArrayList ();` is telling me "type List does not take parameters". – Tam Coton Jul 11 '14 at 13:46
  • About super() - you are lacking basic knowledge about Java language, reading this might help you to understand this particular case: http://docs.oracle.com/javase/tutorial/java/IandI/super.html – Mikle Garin Jul 11 '14 at 13:49
  • About "type List does not take parameters" - it seems that IDE you are using have imported class `java.awt.List` instead of `java.util.List` - I have faced this issue a lot, just replace the import of `java.awt.List` with `java.util.List` and error will be gone. – Mikle Garin Jul 11 '14 at 13:51
  • Ah, thanks! I found about super being used to call methods of the superclass, but not the case where it was calling the constructor method. – Tam Coton Jul 11 '14 at 14:04
  • super() call inside constructor refers to superclass constructor, it is actually not necessary in your example, but I prefer having them added in the code – Mikle Garin Jul 11 '14 at 14:22
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/57162/discussion-between-tam-coton-and-mikle-garin). – Tam Coton Jul 11 '14 at 14:23
1

If you want to draw independently of the presentation / rendering, you can draw on a BufferedImage for example. You can acquire a Graphics object associated with the BufferedImage with its getGraphics() and createGraphics() methods.

And if later you want to display this, you can with the Graphics.drawImage() methods inside JComponent.paintComponent().

icza
  • 389,944
  • 63
  • 907
  • 827