Moving Eyes by Gilbert Le Blanc

This is a short example of a Java Swing GUI with drawing and simple animation. The eyeballs in the GUI follow the mouse cursor as you move the cursor in the drawing area of the GUI.

Introduction

I used Windows 10 and Java 7 to create this GUI. My screen capture software doesn’t recognize the narrow borders of a Windows 10 window.

I wrote 4 Java classes for this simple example. I put the 4 Java classes together to paste as a single example. You should put these classes in separate files when you create your Swing GUI.

I used the model / view / controller model when creating this Swing GUI. This means that:

  1. The view may read values from the model.
  2. The view may not update the model.
  3. The controller will update the model.
  4. The controller will repaint / revalidate the view.

Basically, the model is ignorant of the view and controller. This allows you to change the view and controller from Swing to a web site, or an Android app.

The model / view / controller pattern allows you to focus on one part of the Swing GUI at a time. In general, you’ll create the model first, then the view, and finally the controllers. You will have to go back and add fields to the model. I guarantee that you’ll come up with something you didn’t think of when you created the first cut of the model classes.

Here’s the code:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

//http://java-articles.info/articles/?p=788
public class MovingEyes implements Runnable {

    private static final int drawingWidth = 400;
    private static final int drawingHeight = 400;
    private static final int eyeballHeight = 150;
    private static final int eyeballWidthMargin = 125;

    private DrawingPanel drawingPanel;

    private Eye[] eyes;

    private JFrame frame;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new MovingEyes());
    }

    public MovingEyes() {
        this.eyes = new Eye[2];
        this.eyes[0] = new Eye(new Point(eyeballWidthMargin, eyeballHeight));
        this.eyes[1] = new Eye(new Point(drawingWidth - eyeballWidthMargin,
                eyeballHeight));
    }

    public void run() {
        frame = new JFrame("Moving Eyes by Gilbert Le Blanc");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        drawingPanel = new DrawingPanel(drawingWidth, drawingHeight);
        frame.add(drawingPanel);

        frame.setSize(800, 600);
    
        frame.setLocationRelativeTo(null);
    
        //frame.setResizable(false);
        // frame.pack();
        // frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public class DrawingPanel extends JPanel {

        private static final long serialVersionUID = -2977860217912678180L;

        private static final int eyeballOuterRadius = 50;
        private static final int eyeballInnerRadius = 20;

        public DrawingPanel(int width, int height) {
            this.addMouseMotionListener(new EyeballListener(this,
                    eyeballOuterRadius - eyeballInnerRadius - 5));
            this.setBackground(Color.WHITE);
            this.setPreferredSize(new Dimension(width, height));
        }

        // images
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            g.setColor(Color.BLACK);

            for (Eye eye : eyes) {
                drawCircle(g, eye.getOrigin(), eyeballOuterRadius);
                fillCircle(g, eye.getEyeballOrigin(), eyeballInnerRadius);
            }
        }

        private void drawCircle(Graphics g, Point origin, int radius) {
            g.drawOval(origin.x - radius, origin.y - radius, radius + radius,
                    radius + radius);
        }

        private void fillCircle(Graphics g, Point origin, int radius) {
            g.fillOval(origin.x - radius, origin.y - radius, radius + radius,
                    radius + radius);
        }

    }

    public class Eye {
        private final Point origin;
        private Point eyeballOrigin;

        public Eye(Point origin) {
            this.origin = origin;
            this.eyeballOrigin = origin;
        }

        public Point getEyeballOrigin() {
            return eyeballOrigin;
        }

        public void setEyeballOrigin(Point eyeballOrigin) {
            this.eyeballOrigin = eyeballOrigin;
        }

        public Point getOrigin() {
            return origin;
        }

    }

    public class EyeballListener extends MouseMotionAdapter {

        private final double eyeballDistance;

        private final DrawingPanel drawingPanel;

        public EyeballListener(DrawingPanel drawingPanel, double eyeballDistance) {
            this.drawingPanel = drawingPanel;
            this.eyeballDistance = eyeballDistance;
        }


        // mouse detector
        public void mouseMoved(MouseEvent event) {
            Point p = event.getPoint();
            for (Eye eye : eyes) {
                Point origin = eye.getOrigin();
                double theta = Math.atan2((double) (p.y - origin.y),
                        (double) (p.x - origin.x));
                int x = (int) Math.round(Math.cos(theta) * eyeballDistance)
                        + origin.x;
                int y = (int) Math.round(Math.sin(theta) * eyeballDistance)
                        + origin.y;
                eye.setEyeballOrigin(new Point(x, y));
            }

            drawingPanel.repaint();
        }

    }

}

You can use the JFrame and DrawingPanel code as a base for any Swing GUI where you need to draw.

Model

The Eye class is a Java object that holds the origin of the eye (circle) and the origin of the eyeball. The Eye class is the model in this simple example.

View

The MovingEyes class is the class that defines the JFrame. The MovingEyes class is part of the view. The main method of this class invokes the SwingUtilities invokeLater method to ensure that the Swing components are defined and modified on the Event Dispatch thread.

We use a JFrame. We do not extend a JFrame. The only time you extend a Swing component, or any Java class, is when you want to override one of the class methods. We’ll see this when I talk about the DrawingPanel.

The constructor of the MovingEyes class defines 2 instances of the Eye class. The run method defines the JFrame. The code in the run method will be similar for all Swing GUIs.

The DrawingPanel class makes up the rest of the view. The DrawingPanel class extends JPanel because we want to override the paintComponent method. The constructor of the DrawingPanel class sets the preferred size of the drawing area, and adds the mouse motion listener. The mouse motion listener is the controller of this Swing GUI.

The paintComponent method of the DrawingPanel class first calls the super paintComponent method. This maintains the Swing paint chain, and should always be the first statement of the overwritten paintComponent method.

The rest of the code in the paintComponent method of the DrawingPanel class draws the eyes. We only have drawing (painting) code in the paintComponent method. Control code belongs in the controller.

Controller

The EyeballListener class is the controller class. You can have more than one controller class in a more complicated Swing GUI.

The EyeballListener class extends the MouseMotionAdapter. You can implement the MouseMotionListener. I’m overriding one method, so the code is shorter when I extend the MouseMotionAdapter.

The mouseMoved method of the EyeballListener class fires a MouseEvent when the mouse is moved. We calculate a new position for the center of an eyeball by finding the theta angle from the center of the eye to the mouse position. The theta angle is used to calculate the new center of the eyeball.

Each Eye instance is updated separately in the for loop. After both eyes are updated, the drawing panel is repainted. This happens so fast that there’s no need for an animation loop in a separate thread.

An animation loop updates the model, draws the view, and waits for a specified period of time. You would use a separate thread for the animation loop, so that the GUI on the Event Dispatch thread stays responsive. If your GUI is not responsive, you’re probably doing too much work on the Event Dispatch thread.

Site: http://java-articles.info/articles/?p=788
Java archive: Moving Eyes by Gilbert Le Blanc.jar
Link: https://www.4shared.com/file/Qm-sr08-ei/Moving_Eyes_by_Gilbert_Le_Blan.html

Anúncios

Protetor De Tela Em Java

Dica de um descanso de tela em Java, e aproveitando para estudar random e manipulação de imagens aleatórias e outras coisinhas a mais em Java claro. Duas classes foram usadas para fazer essa aplicação.

1ª Classe

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Vector;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Aquarium extends JPanel {

private static final long serialVersionUID = 1L;
public static final int NUMNBER_OF_FISHIES = 12;
private BufferedImage masterFish;
private Vector<Fish> fishes = new Vector<Fish>(NUMNBER_OF_FISHIES);
private int sleepTime = 110;

public Aquarium() {
// set the title and assign tracker object
try {
masterFish = ImageIO.read(getClass().getResource(
"/imagens/fish2.png"));
} catch (IOException iOException) {
iOException.printStackTrace();
}

// This is a little cheat which means you only ever need one fish image
// Basically it will flip the master along it's horizontal axies ;)
BufferedImage flippedMaster = new BufferedImage(masterFish.getWidth(),
masterFish.getHeight(), masterFish.getType());
Graphics2D g2d = flippedMaster.createGraphics();
g2d.setTransform(AffineTransform.getScaleInstance(-1, 1));
g2d.drawImage(masterFish, -masterFish.getWidth(), 0, this);
g2d.dispose();

for (int index = 0; index < NUMNBER_OF_FISHIES; index++) {
fishes.add(new Fish(masterFish, flippedMaster, this));
}

Thread background = new Thread(new Background());
background.setDaemon(true);
background.start();
}

public static void main(String ar[]) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager
.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException classNotFoundException) {
} catch (InstantiationException instantiationException) {
} catch (IllegalAccessException illegalAccessException) {
} catch (UnsupportedLookAndFeelException unsupportedLookAndFeelException) {
}

JFrame frame = new JFrame("The Aquarium - J. Marcos B.");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new Aquarium());
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}

protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Fish fish : fishes) {
fish.drawFishImage(g);
}
}

protected class Background implements Runnable {
@Override
public void run() {
while (true) {

for (Fish fish : fishes) {
fish.swim();
}

try {
Thread.sleep(sleepTime);
} catch (Exception e) {
System.out.println(e.getMessage());
}
repaint();
}
}
}
}

2ª Classe

import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.Random;

public class Fish {

private Component tank;
private Image image1, image2;
private Point location, velocity;
private Random random;

public Fish(Image image1, Image image2, Component tank) {
random = new Random();
this.tank = tank;
this.image1 = image1;
this.image2 = image2;
this.location = new Point(100 + (Math.abs(random.nextInt()) % 300),
100 + Math.abs(random.nextInt()) % 100);
this.velocity = new Point(random.nextInt() % 8, random.nextInt() % 8);
System.out.println(location);
}

public void swim() {
Rectangle edges = tank.getBounds();

// determine the optimum velocity to make the fish swim
if (random.nextInt() % 7 <= 1) {
velocity.x += random.nextInt() % 4;
velocity.x = Math.min(velocity.x, 8);
velocity.x = Math.max(velocity.x, -8);
velocity.y += random.nextInt() % 4;
velocity.y = Math.min(velocity.y, 8);
velocity.y = Math.max(velocity.y, -8);
}

// add the velocity to the location of the fish to make it move
location.x += velocity.x;
location.y += velocity.y;

if (location.x < edges.x) {
location.x = edges.x;
velocity.x = -velocity.x;
}

if ((location.x + image1.getWidth(tank)) > (edges.x + edges.width)) {
location.x = edges.x + edges.width - image1.getWidth(tank);
velocity.x = -velocity.x;
}

if (location.y < edges.y) {
location.y = edges.y;
velocity.y = -velocity.y;
}

if ((location.y + image1.getHeight(tank)) > (edges.y + edges.height)) {
location.y = edges.y + edges.height - image1.getHeight(tank);
velocity.y = -velocity.y;
}
}

public void drawFishImage(Graphics g) {
if (velocity.x < 0) {
g.drawImage(image1, location.x, location.y, tank);
} else {
g.drawImage(image2, location.x, location.y, tank);
}
}
}

Pronto agora quem não sabe programar e só quer o aplicativo pra rodar em sua área de trabalho aqui vai o link para baixar.

The Aquarium.jar

https://docs.google.com/open?id=0B4NdXxwTpKVwNGlaNElmZjlqdEk