I am working on this reaction time game that tells you to click an arrow key once the ball has turned into a different colored ball. However, I can't seem to get the image of the ball to be replaced by the other ball.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.Timer;
public class Game extends JPanel
{
private JLabel watch, main;
private ImageIcon constant, react;
final int width = 600;
final int height = 600;
private Timer replace;
private ActionListener timerListener;
public Game()
{
setPreferredSize(new Dimension(width, height));
setBackground(Color.black);
watch = new JLabel("Click Up Arrow when you see a blue ball");
watch.setForeground(Color.white);
add(watch);
constant = new ImageIcon("constantCircle.png");
main = new JLabel(constant);
replace = new Timer(3000, timerListener);
replace.setRepeats(false);
replace.start();
add(main);
}
public void actionPerformed (ActionEvent e)
{
react = new ImageIcon("reactCircle.png");
main.setIcon(react);
}
}
This is the code for my display and I wanted to use a swing timer to replace the image after 3 seconds
This is what I want it to look like before
and this is what I want it to look like after 3 seconds
You're never initializing timerListener
private ActionListener timerListener;
Inside your constructor you have to call (With Java 8 lambdas):
timerListener = e -> {
react = new ImageIcon("reactCircle.png");
main.setIcon(react);
}
Or (Java 7 and lower):
timerListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
react = new ImageIcon("reactCircle.png");
main.setIcon(react);
}
}
And don't forget to call timerListener.stop() once the timer has fired, so that you don't keep computing more times
From Andrew Thompson's comment below:
As you only want to replace the image once, call timerListener.setRepeats(false) on your constructor. Check the docs for more information about it.
Related
So I am making a game that records your reaction time after you see something pop up on the screen, but I am having trouble with getting that reaction time. I want the user to press the up arrow key once they see a blue ball and I want to record their reaction time once they pressed that button.
Here is my code:
public class Game extends JPanel
{
private JLabel start, main, time;
private ImageIcon constant, react;
final int width = 600;
final int height = 600;
private Timer replace;
private Random random;
private int randTime;
private long startTime;
private long stopTime;
private long reactionTime;
private Action upAction;
public Game()
{
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setPreferredSize(new Dimension(width, height));
setBackground(Color.black);
start = new JLabel("Click Up Arrow when you see a blue ball");
start.setForeground(Color.white);
start.setAlignmentX(Component.CENTER_ALIGNMENT);
add(start);
constant = new ImageIcon("constantCircle.png");
main = new JLabel(constant);
main.setAlignmentX(Component.CENTER_ALIGNMENT);
randomTime();
replace = new Timer(randTime, timeListener);
startTime = System.currentTimeMillis();
replace.setRepeats(false);
replace.start();
add(main);
time = new JLabel("0");
time.getInputMap().put(KeyStroke.getKeyStroke("UP"), "upAction");
time.getActionMap().put("upAction", upAction);
add(time);
}
public void randomTime()
{
random = new Random();
int max = 8000;
randTime = random.nextInt(max);
}
ActionListener timeListener = new ActionListener()
{
public void actionPerformed (ActionEvent e)
{
react = new ImageIcon("reactCircle.png");
main.setIcon(react);
}
};
public class UpAction extends AbstractAction
{
public void actionPerformed(ActionEvent e)
{
stopTime = System.currentTimeMillis();
reactionTime = stopTime - startTime;
time.setText("" + reactionTime);
}
}
}
I setup a "startTime" using System.currentTimeMillis to get the time after the ball turns blue but I am not sure if that is the correct way to do it.
I also setup a "stopTime" in the "UpAction" class where I want to get the time once the user presses up arrow but I it does not work.
if anything doesn't make sense or isn't clear enough, I'll try my best to elaborate more
I came up with the following GUI.
There are two important principles I want to explain. The first is that creating the GUI is a separate process from updating the GUI. The second is that the game process is a state machine. The game is in six separate states. Here's what I wrote to keep the states in mind.
Sequence of events
Left-click button
Wait 2 - 4 seconds to display the circle.
Capture start time
Left-click button
Capture end time.
Calculate and display reaction time.
Repeat 1 - 6.
So, for the GUI, I created a JFrame and three JPanels; an upper JPanel, a drawing JPanel, and a button JPanel.
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.
The JFrame has a default BorderLayout, which I used to place the three JPanels. The JFrame method calls must be executed in a specific order. This is the order I use for all my Swing applications.
The upper JPanel contains the instructions and the reaction time display. A JTextArea is great for displaying instructions. I put the JTextArea inside an inner JPanel using a FlowLayout, which I placed in the upper JPanel using a BorderLayout. Nesting layouts like this is a good way to organize the Swing components in a logical manner.
I put the reaction time Swing components in another inner JPanel, which I placed in the upper JPanel.
I created a drawing JPanel so I wouldn't have to bother with an image.
The button JPanel holds the Submit JButton.
I created two controller classes. One controller class, ButtonListener, responds to the JButton left-clicks. The other controller class, TimerListener, creates the delay for drawing the circle.
The ButtonListener state variable allows me to provide different functionality with the same ActionListener. If you wish, you can write separate ActionListener classes, one for each function.
By separating my code into view and controller classes, I could separate my concerns and focus on one part of the application at a time.
Here's the complete runnable code. I made all the classes inner classes so I could post this code as one block.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class ReactionTimeGame implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new ReactionTimeGame());
}
private long reactionTime;
private DrawingPanel drawingPanel;
private JTextField reactionTimeField;
public ReactionTimeGame() {
this.reactionTime = 0L;
}
#Override
public void run() {
JFrame frame = new JFrame("Reaction Time Game");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createUpperPanel(), BorderLayout.BEFORE_FIRST_LINE);
this.drawingPanel = new DrawingPanel();
frame.add(drawingPanel, BorderLayout.CENTER);
frame.add(createButtonPanel(), BorderLayout.AFTER_LAST_LINE);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createUpperPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JPanel innerPanel = new JPanel(new FlowLayout());
String instructions = "This game will test your reaction time. To play "
+ "the game, left-click on the Submit button. After a random time "
+ "from 2 - 4 seconds, a circle will appear. Left-click the "
+ "Submit button again. Your reaction time will be displayed "
+ "above where the circle was.\n\n"
+ "Left-click the Submit button to start each round of the game.";
JTextArea textArea = new JTextArea(7, 40);
textArea.setEditable(false);
textArea.setText(instructions);
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
innerPanel.add(textArea);
panel.add(innerPanel, BorderLayout.BEFORE_FIRST_LINE);
innerPanel = new JPanel(new FlowLayout());
JLabel label = new JLabel("Reaction Time:");
innerPanel.add(label);
reactionTimeField = new JTextField(5);
reactionTimeField.setEditable(false);
updateReactionTime();
innerPanel.add(reactionTimeField);
label = new JLabel("seconds");
innerPanel.add(label);
panel.add(innerPanel, BorderLayout.AFTER_LAST_LINE);
return panel;
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel(new FlowLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JButton button = new JButton("Submit");
button.addActionListener(new ButtonListener());
panel.add(button);
return panel;
}
public void setReactionTime(long reactionTime) {
this.reactionTime = reactionTime;
}
public void drawCircle() {
drawingPanel.setDrawCircle(true);
drawingPanel.repaint();
}
public void eraseCircle() {
drawingPanel.setDrawCircle(false);
drawingPanel.repaint();
}
public void updateReactionTime() {
double time = 0.001 * reactionTime;
reactionTimeField.setText(String.format("%.3f", time));
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private boolean drawCircle;
public DrawingPanel() {
this.drawCircle = false;
this.setPreferredSize(new Dimension(300, 300));
}
public void setDrawCircle(boolean drawCircle) {
this.drawCircle = drawCircle;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (drawCircle) {
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
int radius = Math.min(getWidth(), getHeight()) * 9 / 20;
int diameter = radius + radius;
g.setColor(Color.MAGENTA);
g.fillOval(centerX - radius, centerY - radius, diameter, diameter);
}
}
}
public class ButtonListener implements ActionListener {
private int state;
private long startTime;
private final Random random;
private Timer timer;
public ButtonListener() {
this.state = 1;
this.random = new Random();
}
#Override
public void actionPerformed(ActionEvent event) {
switch (state) {
case 1:
int delay = random.nextInt(2000) + 2000;
timer = new Timer(delay, new TimerListener(this));
timer.start();
state = 2;
break;
case 2:
setEndTime(System.currentTimeMillis());
eraseCircle();
state = 1;
break;
}
}
public int getState() {
return state;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public void setEndTime(long endTime) {
long elapsedTime = endTime - startTime;
setReactionTime(elapsedTime);
updateReactionTime();
}
}
public class TimerListener implements ActionListener {
private final ButtonListener listener;
public TimerListener(ButtonListener listener) {
this.listener = listener;
}
#Override
public void actionPerformed(ActionEvent event) {
Timer timer = (Timer) event.getSource();
timer.stop();
if (listener.getState() == 2) {
listener.setStartTime(System.currentTimeMillis());
drawCircle();
}
}
}
}
I have written this code however I am having trouble with one aspect I wish to code into it. I want to make the green square change size when I press one of the three buttons I have so when I press the button 'small' the square changes size to small e.g. 100 and when I press the button 'medium' it changes size to medium e.g. 400. This is my code so far:
package Lab2;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Main {
public static void main(String[] args) {
FilledFrame frame = new FilledFrame();
frame.setVisible( true );
}
}
class FilledFrame extends JFrame {
int size = 400;
public FilledFrame()
{
JButton butSmall = new JButton("Small");
JButton butMedium = new JButton("Medium");
JButton butLarge = new JButton("Large");
JButton butMessage = new JButton("Say Hi");
SquarePanel panel = new SquarePanel(this);
JPanel butPanel = new JPanel();
butPanel.add(butSmall);
butPanel.add(butMedium);
butPanel.add(butLarge);
butPanel.add(butMessage);
add(butPanel, BorderLayout.NORTH);
add(panel, BorderLayout.CENTER);
setSize( size+100, size+100 ); } }
class SquarePanel extends JPanel {
FilledFrame theApp;
SquarePanel(FilledFrame app)
{
theApp = app;
}
public void paintComponent ( Graphics g)
{
super.paintComponent(g);
g.setColor(Color.green);
g.fillRect(20, 20, theApp.size, theApp.size);
}
}
class buttonHandler implements ActionListener {
FilledFrame theApp;
int size;
public buttonHandler(FilledFrame app, int size) {
theApp = app;
this.size = size;
}
#Override
public void actionPerformed (ActionEvent e){
theApp.setSize(this.size, this.size);
}
}
As I don't see any event listeners for your buttons, I assume this is all the code you have. Your buttons will not do anything unless you tell them to do it. You need to add event listeners, and through that change the size and update the panel.
Example:
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
theApp.size = 200;
frame.getContentPane().repaint();
//OR frame.repaint();
}
});
EDIT:
The problem with using the button handler class is you would need to find which buttton was pressed, instead its easier to use the way I showed above. I edited the code above, try copy pasting to one of the buttons.
Is it possible to add more than 1 mouselistener to a JButton? You know when I click on the button it should change color and text, and do something (e.g system.out.println), and when I click it again it should go back to the previous state, and print something else.
What I've tried:
JButton b = new JButton("First");
b.setBackground(Color.GREEN);
b.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e)
{
b.setBackground(Color.RED);
b.setText("Second");
System.out.println("You've clicked the button");
}
if(b.getModel.isPressed){
b.setBackground(Color.GREEN);
b.setText("Second");
System.out.println("You're back");
}
The problem is that the button doesn't go back to the previous state with the color (green) and text, and I don't how to handle that.
First of all, you shouldn't be using a MouseListener to do these things, because a better listener, ActionListener, was built specifically to be used with JButtons and similar entities to notify programs that a button has been pressed.
Having said that, sure you can add multiple ActionListeners (or MouseListeners) to a JButton, or you can have an ActionListener change its behaviors depending on the state of the program (usually meaning the values held by fields of the class).
A problem with your code and question is that I don't see when you expect or want the button to change its color back to green. If after a certain period of time, then have your ActionListener start a Swing Timer that changes the button's color back to green after x milliseconds.
Edit: I see, you want to toggle color -- then use a boolean field that you toggle or check the button's current color and base the listener's response based on that color.
example
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
#SuppressWarnings("serial")
public class ToggleColor extends JPanel {
public ToggleColor() {
JButton button = new JButton(new MyButtonAction());
button.setBackground(Color.GREEN);
add(button);
}
private static void createAndShowGui() {
ToggleColor mainPanel = new ToggleColor();
JFrame frame = new JFrame("ToggleColor");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
#SuppressWarnings("serial")
class MyButtonAction extends AbstractAction {
// !! parallel arrays being used below -- avoid if possible
private static final String[] TEXT = {"First", "Second", "Third"};
private static final Color[] COLORS = {Color.GREEN, Color.RED, new Color(108, 160, 220)};
private int index = 0;
public MyButtonAction() {
super(TEXT[0]);
}
#Override
public void actionPerformed(ActionEvent e) {
index++;
index %= TEXT.length;
putValue(NAME, TEXT[index]);
Component c = (Component) e.getSource();
c.setBackground(COLORS[index]);
}
}
This uses an AbstractAction class which is like an ActionListener but on "steroids"
You should only register one lister, but that listener will maintain some state regarding the number for mouse clicks. A simple if/else block will change the actions and change the text on the button label.
I am beginner to Swing and I am writing some codes. I have added images on JLabel and JLabel on JFrame. I want to move that Image. Is it possible to move that image(fish). If yes, How can I do it?
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Aquarium extends JFrame{
private ImageIcon fish2image;
private ImageIcon fish1image;
private ImageIcon image;
private JLabel imglabel;
private JLabel fish1label;
private JLabel fish2label;
int numberFish = 12;
public Aquarium(){
initComponents();
setTitle("The Aquarium");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setResizable(false);
setBounds(500,200,500,300); //left,top,width,height
add(fish1label);
add(fish2label);
add(imglabel);
}
private void initComponents(){
image = new ImageIcon(getClass().getResource("../res/aquarium.gif"));
fish1image = new ImageIcon(getClass().getResource("../res/smallFish.gif"));
fish2image = new ImageIcon(getClass().getResource("../res/smallFish2.gif"));
imglabel = new JLabel(image);
fish1label = new JLabel(fish1image);
fish2label = new JLabel(fish2image);
fish1label.setBounds(100,100,250,200);
fish2label.setBounds(50,25,200,200);
}
public static void main(String[] args) {
new Aquarium();
}
}
Repeat question. But you would shift the x, and y values (Based on keyBindings or KeyListener added on frame) and it will automatically repaint.
But since you want the fist to move automatically. You are going to need either a loop based on a swing timer, Thread.sleep(1000); <= Highly not recommended, or a Thread.
Then you would shift the x and y values as described above as a certain amount of seconds pass within the method that you choose
So I am creating a basic application that I want to have a JLabel at the bottom of the screen that starts at the left bottom corner and moves, animation style, to the right bottom corner in a set time, and a static image in the center. To do this, I created a JFrame with a JPanel using BorderLayout. There is a JLabel with an ImageIcon added to BorderLayout.CENTER and a JPanel at BorderLayout.SOUTH. My code, while hastily written and far from pretty, is:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.BorderFactory;
public class GameWindow extends JPanel{
private static JLabel mainWindow, arrowLabel, arrowBox;
protected static JFrame frame;
protected static JPanel arrows;
public static int x = 600;
public GameWindow(){
mainWindow = new JLabel("Center");
arrowLabel = new JLabel("Moving");
arrows = new JPanel();
arrows.setSize(600, 100);
arrows.setLayout(null);
arrowBox = new JLabel("");
arrowBox.setBounds(0, 0, 150, 100);
arrowBox.setPreferredSize(new Dimension(150, 100));
arrowBox.setBorder(BorderFactory.createLineBorder(Color.black));
arrows.add(arrowBox);
this.setSize(600,600);
this.setLayout(new BorderLayout());
this.add(mainWindow, BorderLayout.CENTER);
this.add(arrows, BorderLayout.SOUTH);
}
public static void main(String[] args)
{
GameWindow g = new GameWindow();
frame = new JFrame("Sword Sword Revolution");
frame.add(g);
frame.setSize(600,600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Timer t = new Timer(1000, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
arrows.add(arrowLabel);
arrowLabel.setBounds(x, 100, 100, 100);
x-=50;
arrows.repaint();
frame.repaint();
}
});
t.start();
}
}
The ImageIcon in the center JLabel appears fine, and the empty JLabel with a border appears at the bottom, but I cannot get the second JLabel with the arrow image to show up on screen. Eventually I will change to scheduleAtFixedRate to continuously move the JLabel, but right now I can't even get the image to appear on screen.
I also understand that I will most likely not be able to use FlowLayout for this, as I understand it does not allow you to set the location of your components. I tried using null layout, but with null layout the empty JLabel with a border does not appear. I can barely make out the top of the border at the bottom edge of the frame, but even with setLocation I cannot get it to appear where I want it to.
Obviously, my thought process is flawed, so any help would be appreciated.
Your use of threading is all wrong for Swing applications. You should not be trying to add or remove components in a background thread but instead should use a Swing Timer to do this on the Swing event thread.
Also, what do you mean by:
I want to have a scrolling JLabel at the bottom of the screen
Please clarify the effect you're trying to achieve.
Also regarding,
I also understand that I will most likely not be able to use FlowLayout for this, as I understand it does not allow you to set the location of your components. I tried using null layout, but with null layout the empty JLabel with a border does not appear. I can barely make out the top of the border at the bottom edge of the frame, but even with setLocation I cannot get it to appear where I want it to.
No, don't use null layout for this situation. There are much better layout managers that can help you build your application in a cleaner more platform-independent manner.
Edit 3
Regarding:
To clarify, at the bottom of the screen I want a JLabel at the far right corner, then in the swing timer, the JLabel will gradually move to the left until it leaves the screen. If I could get setLocation to work, the basic premise would be to have a variable x set to 600, and then every second decrement x by say 50 and then redraw the JLabel at the new location on the screen. Basic animation.
I would create a JPanel for the bottom of the screen for the purposes of either holding your JLabel or displaying the image without a JLabel by overriding its paintComponent(...) method. If you use it as a container, then yes, its layout should be null, but the rest of the GUI should not be using null layout. The Swing Timer would simply change the JLabel's location and then call repaint() on its JPanel/container. If you go the latter route, you would draw the image in the JPanel's paintComponent(...) method using g.drawImage(myImage, x, y), and your timer would change x and/or y and call repaint() on the drawing JPanel.
Also, you likely do not want to keep adding a JLabel in your timer but rather simply moving the JLabel that's already displayed in the GUI.
Also, to avoid focus issues, don't use a KeyListener to capture keystroke input but rather use Key Bindings. Google will direct you to a great tutorial on this construct.
Edit 4
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.EnumMap;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class AnimateExample extends JPanel {
public static final String DUKE_IMG_PATH =
"https://duke.kenai.com/iconSized/duke.gif";
private static final int PREF_W = 800;
private static final int PREF_H = 800;
private static final int TIMER_DELAY = 20;
private static final String KEY_DOWN = "key down";
private static final String KEY_RELEASE = "key release";
public static final int TRANSLATE_SCALE = 3;
private static final String BACKGROUND_STRING = "Use Arrow Keys to Move Image";
private static final Font BG_STRING_FONT = new Font(Font.SANS_SERIF,
Font.BOLD, 32);
private EnumMap<Direction, Boolean> dirMap =
new EnumMap<AnimateExample.Direction, Boolean>(Direction.class);
private BufferedImage image = null;
private int imgX = 0;
private int imgY = 0;
private int bgStringX;
private int bgStringY;
public AnimateExample() {
for (Direction dir : Direction.values()) {
dirMap.put(dir, Boolean.FALSE);
}
try {
URL imgUrl = new URL(DUKE_IMG_PATH);
image = ImageIO.read(imgUrl);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
new Timer(TIMER_DELAY, new TimerListener()).start();
// here we set up our key bindings
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(condition);
ActionMap actionMap = getActionMap();
for (final Direction dir : Direction.values()) {
// for the key down key stroke
KeyStroke keyStroke = KeyStroke.getKeyStroke(dir.getKeyCode(), 0,
false);
inputMap.put(keyStroke, dir.name() + KEY_DOWN);
actionMap.put(dir.name() + KEY_DOWN, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
dirMap.put(dir, true);
}
});
// for the key release key stroke
keyStroke = KeyStroke.getKeyStroke(dir.getKeyCode(), 0, true);
inputMap.put(keyStroke, dir.name() + KEY_RELEASE);
actionMap.put(dir.name() + KEY_RELEASE, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
dirMap.put(dir, false);
}
});
}
FontMetrics fontMetrics = getFontMetrics(BG_STRING_FONT);
int w = fontMetrics.stringWidth(BACKGROUND_STRING);
int h = fontMetrics.getHeight();
bgStringX = (PREF_W - w) / 2;
bgStringY = (PREF_H - h) / 2;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g.setFont(BG_STRING_FONT);
g.setColor(Color.LIGHT_GRAY);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.drawString(BACKGROUND_STRING, bgStringX, bgStringY);
if (image != null) {
g.drawImage(image, imgX, imgY, this);
}
}
private class TimerListener implements ActionListener {
public void actionPerformed(java.awt.event.ActionEvent e) {
for (Direction dir : Direction.values()) {
if (dirMap.get(dir)) {
imgX += dir.getX() * TRANSLATE_SCALE;
imgY += dir.getY() * TRANSLATE_SCALE;
}
}
repaint();
};
}
enum Direction {
Up(KeyEvent.VK_UP, 0, -1), Down(KeyEvent.VK_DOWN, 0, 1), Left(
KeyEvent.VK_LEFT, -1, 0), Right(KeyEvent.VK_RIGHT, 1, 0);
private int keyCode;
private int x;
private int y;
private Direction(int keyCode, int x, int y) {
this.keyCode = keyCode;
this.x = x;
this.y = y;
}
public int getKeyCode() {
return keyCode;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
private static void createAndShowGui() {
AnimateExample mainPanel = new AnimateExample();
JFrame frame = new JFrame("Animate Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Which will create this GUI:
Three possibilities:
You can either use a library like SlidingLayout to create such transition with very few lines of code. You won't be able to tweak the animation but your life will be easier.
You can use an animation engine like the Universal Tween Engine to configure everything by hand and tweak the animation as much as you want (the first lib uses this one under the hood). Using such engine, you can animate what you want: positions, colors, font size, ...
You can code everything by hand and embrace the hell that is the animation world :)
In the end, you'll be able to quickly create animations like these (it's a tool I'm currently working on, used to configure eclipse projects for android dev using the LibGDX game framework):
I made these libraries to ease the pain that is UI animation (I love UI design :p). I released them open-source (free to use, license apache-2), hoping they may help some people too.
If you need help, there is a dedicated help forum for each library.
Very simple, look at this:
javax.swing.JLabel lb = new javax.swing.JLabel();
Image image = Toolkit.getDefaultToolkit().createImage("Your animated GIF");
ImageIcon xIcon = new ImageIcon(image);
xIcon.setImageObserver(this);
lb.setIcon(xIcon);