Animate JPAnel (Slide In) with Timer

animate JPanel (slide in) with timer

The timer should be changing the location on each tick, until it is in place, instead, on each tick, you're running through a for-next loop, which is blocking the EDT until the loop finishes, preventing from updating the UI

Update with example

For example...

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestAnimatedPane {

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

public TestAnimatedPane() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}

JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}

public class TestPane extends JPanel {

private JPanel panel;

public TestPane() {
setLayout(null);
panel = new JPanel();
panel.setBackground(Color.RED);
add(panel);
Dimension size = getPreferredSize();

Rectangle from = new Rectangle(size.width, (size.height - 50) / 2, 50, 50);
Rectangle to = new Rectangle((size.width - 50) / 2, (size.height - 50) / 2, 50, 50);

Animate animate = new Animate(panel, from, to);
animate.start();

}

@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}

}

public static class Animate {

public static final int RUN_TIME = 2000;

private JPanel panel;
private Rectangle from;
private Rectangle to;

private long startTime;

public Animate(JPanel panel, Rectangle from, Rectangle to) {
this.panel = panel;
this.from = from;
this.to = to;
}

public void start() {
Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
long duration = System.currentTimeMillis() - startTime;
double progress = (double)duration / (double)RUN_TIME;
if (progress > 1f) {
progress = 1f;
((Timer)e.getSource()).stop();
}
Rectangle target = calculateProgress(from, to, progress);
panel.setBounds(target);
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.setInitialDelay(0);
startTime = System.currentTimeMillis();
timer.start();
}

}

public static Rectangle calculateProgress(Rectangle startBounds, Rectangle targetBounds, double progress) {

Rectangle bounds = new Rectangle();

if (startBounds != null && targetBounds != null) {

bounds.setLocation(calculateProgress(startBounds.getLocation(), targetBounds.getLocation(), progress));
bounds.setSize(calculateProgress(startBounds.getSize(), targetBounds.getSize(), progress));

}

return bounds;

}

public static Point calculateProgress(Point startPoint, Point targetPoint, double progress) {

Point point = new Point();

if (startPoint != null && targetPoint != null) {

point.x = calculateProgress(startPoint.x, targetPoint.x, progress);
point.y = calculateProgress(startPoint.y, targetPoint.y, progress);

}

return point;

}

public static int calculateProgress(int startValue, int endValue, double fraction) {

int value = 0;
int distance = endValue - startValue;
value = (int)Math.round((double)distance * fraction);
value += startValue;

return value;

}

public static Dimension calculateProgress(Dimension startSize, Dimension targetSize, double progress) {

Dimension size = new Dimension();

if (startSize != null && targetSize != null) {

size.width = calculateProgress(startSize.width, targetSize.width, progress);
size.height = calculateProgress(startSize.height, targetSize.height, progress);

}

return size;

}
}

Update

I should have added this in last night (1 year who didn't want to go to bed, 2 parents that did, say no more...)

Animation is complex topic, especially when you start looking at variable speed (the example is static).

Instead of reinventing the wheel, you should seriously consider taking a look at...

  • Timing Framework - This is base animation framework, that makes no assumptions about how you might like to use it.
  • Trident - Similar to the Timing Framework, but also has support for Swing based components (via reflection) build in
  • Universal Tween Engine

JAVA JPanel sliding out and sliding in at the same time

Before coding the below, I used Microsoft Paint to make 7 rectangles of different colors, 60 pixels wide and 90 pixels tall. Where you see jf.setSize(186,120) I only got these values by trial and error. My timerDelay i just guessed would be an appropriate value. All other values should be obvious as to why.

The key to all of this is that in the TimerActionListener I simply getLocation() of each tiles[i], add 5 to the x co-ordinate, and reset it to -120 when it reaches 300, then setLocation(int, int) again.

package SlidingTiles;

import ...;

public class Main
{
private static int timerDelay = 100;
private static Timer t = new Timer(timerDelay, new TimerActionListener());

private static JFrame jf = new JFrame();
private static JPanel jp = new JPanel();
private static String[] colors = { "blue", "brown", "green", "grey", "purple", "red", "yellow" };
private static JLabel[] tiles = new JLabel[7];

public static void main(String[] args)
{

for(int i=0; i<7; ++i)
{
tiles[i] = new JLabel(new ImageIcon("tiles/"+colors[i]+".jpg"));
tiles[i].setBounds(-120+60*i,0,60,90);
jf.add(tiles[i]);
}

jf.add(jp);
jf.setSize(186,120);
jf.setResizable(false);
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
t.start();
}

public static class TimerActionListener implements ActionListener
{
@Override
public void actionPerformed(ActionEvent e)
{
for(int i=0; i<7; ++i)
{
Point p = tiles[i].getLocation();
int newX = (int) p.getX()+5;
if(newX == 300)
newX = -120;

tiles[i].setLocation(newX, 0);
}
}
}
}

Java sliding JPanels

Sliding panels can be tricky. Here is some starter code. Modify to fit
your needs. Add error checking and exception handling as necessary.

This example uses JButtons and a JTree as content but you can use just about any type of content.

Example Slider

Usage:

static public void main(final String[] args) throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
final JFrame jFrame = new JFrame() {
{
final PanelSlider42<JFrame> slider = new PanelSlider42<JFrame>(this);
final JPanel jPanel = slider.getBasePanel();

slider.addComponent(new JButton("1"));
slider.addComponent(new JButton("22"));
slider.addComponent(new JButton("333"));
slider.addComponent(new JButton("4444"));

getContentPane().add(jPanel);
setSize(300, 300);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setVisible(true);
}
};
}
});
}

The impl is lengthy ...

package com.java42.example.code;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;

public class PanelSlider42<ParentType extends Container> {

private static final int RIGHT = 0x01;
private static final int LEFT = 0x02;
private static final int TOP = 0x03;
private static final int BOTTOM = 0x04;
private final JPanel basePanel = new JPanel();
private final ParentType parent;
private final Object lock = new Object();
private final ArrayList<Component> jPanels = new ArrayList<Component>();
private final boolean useSlideButton = true;
private boolean isSlideInProgress = false;

private final JPanel glassPane;
{
glassPane = new JPanel();
glassPane.setOpaque(false);
glassPane.addMouseListener(new MouseAdapter() {
});
glassPane.addMouseMotionListener(new MouseMotionAdapter() {
});
glassPane.addKeyListener(new KeyAdapter() {
});
}

public PanelSlider42(final ParentType parent) {
if (parent == null) {
throw new RuntimeException("ProgramCheck: Parent can not be null.");
}
if ((parent instanceof JFrame) || (parent instanceof JDialog) || (parent instanceof JWindow) || (parent instanceof JPanel)) {
}
else {
throw new RuntimeException("ProgramCheck: Parent type not supported. " + parent.getClass().getSimpleName());
}
this.parent = parent;
attach();
basePanel.setSize(parent.getSize());
basePanel.setLayout(new BorderLayout());
if (useSlideButton) {
final JPanel statusPanel = new JPanel();
basePanel.add(statusPanel, BorderLayout.SOUTH);
statusPanel.add(new JButton("Slide Left") {
private static final long serialVersionUID = 9204819004142223529L;
{
setMargin(new Insets(0, 0, 0, 0));
}
{
addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
slideLeft();
}
});
}
});
statusPanel.add(new JButton("Slide Right") {
{
setMargin(new Insets(0, 0, 0, 0));
}
private static final long serialVersionUID = 9204819004142223529L;
{
addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
slideRight();
}
});
}
});
statusPanel.add(new JButton("Slide Up") {
{
setMargin(new Insets(0, 0, 0, 0));
}
private static final long serialVersionUID = 9204819004142223529L;
{
addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
slideTop();
}
});
}
});
statusPanel.add(new JButton("Slide Down") {
{
setMargin(new Insets(0, 0, 0, 0));
}
private static final long serialVersionUID = 9204819004142223529L;
{
addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
slideBottom();
}
});
}
});
}
}

public JPanel getBasePanel() {
return basePanel;
}

private void attach() {
final ParentType w = this.parent;
if (w instanceof JFrame) {
final JFrame j = (JFrame) w;
if (j.getContentPane().getComponents().length > 0) {
throw new RuntimeException("ProgramCheck: Parent already contains content.");
}
j.getContentPane().add(basePanel);
}
if (w instanceof JDialog) {
final JDialog j = (JDialog) w;
if (j.getContentPane().getComponents().length > 0) {
throw new RuntimeException("ProgramCheck: Parent already contains content.");
}
j.getContentPane().add(basePanel);
}
if (w instanceof JWindow) {
final JWindow j = (JWindow) w;
if (j.getContentPane().getComponents().length > 0) {
throw new RuntimeException("ProgramCheck: Parent already contains content.");
}
j.getContentPane().add(basePanel);
}
if (w instanceof JPanel) {
final JPanel j = (JPanel) w;
if (j.getComponents().length > 0) {
throw new RuntimeException("ProgramCheck: Parent already contains content.");
}
j.add(basePanel);
}
}

public void addComponent(final Component component) {
if (jPanels.contains(component)) {
}
else {
jPanels.add(component);
if (jPanels.size() == 1) {
basePanel.add(component);
}
component.setSize(basePanel.getSize());
component.setLocation(0, 0);
}
}

public void removeComponent(final Component component) {
if (jPanels.contains(component)) {
jPanels.remove(component);
}
}

public void slideLeft() {
slide(LEFT);
}

public void slideRight() {
slide(RIGHT);
}

public void slideTop() {
slide(TOP);
}

public void slideBottom() {
slide(BOTTOM);
}

private void enableUserInput(final ParentType w) {
if (w instanceof JFrame) {
((JFrame) w).getGlassPane().setVisible(false);
}
if (w instanceof JDialog) {
((JDialog) w).getGlassPane().setVisible(false);
}
if (w instanceof JWindow) {
((JWindow) w).getGlassPane().setVisible(false);
}
}

private void disableUserInput(final ParentType w) {
if (w instanceof JFrame) {
((JFrame) w).setGlassPane(glassPane);
}
if (w instanceof JDialog) {
((JDialog) w).setGlassPane(glassPane);
}
if (w instanceof JWindow) {
((JWindow) w).setGlassPane(glassPane);
}
glassPane.setVisible(true);
}

private void enableTransparentOverylay() {
if (parent instanceof JFrame) {
((JFrame) parent).getContentPane().setBackground(jPanels.get(0).getBackground());
parent.remove(basePanel);
parent.validate();
}
if (parent instanceof JDialog) {
((JDialog) parent).getContentPane().setBackground(jPanels.get(0).getBackground());
parent.remove(basePanel);
parent.validate();
}
if (parent instanceof JWindow) {
((JWindow) parent).getContentPane().setBackground(jPanels.get(0).getBackground());
parent.remove(basePanel);
parent.validate();
}
}

private void slide(final int slideType) {
if (!isSlideInProgress) {
isSlideInProgress = true;
final Thread t0 = new Thread(new Runnable() {
@Override
public void run() {
parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
disableUserInput(parent);
slide(true, slideType);
enableUserInput(parent);
parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
isSlideInProgress = false;
}
});
t0.setDaemon(true);
t0.start();
}
else {
Toolkit.getDefaultToolkit().beep();
}
}

private void slide(final boolean useLoop, final int slideType) {
if (jPanels.size() < 2) {
System.err.println("Not enough panels");
return;
}
synchronized (lock) {
Component componentOld = null;
Component componentNew = null;
if ((slideType == LEFT) || (slideType == TOP)) {
componentNew = jPanels.remove(jPanels.size() - 1);
componentOld = jPanels.get(0);
jPanels.add(0, componentNew);
}
if ((slideType == RIGHT) || (slideType == BOTTOM)) {
componentOld = jPanels.remove(0);
jPanels.add(componentOld);
componentNew = jPanels.get(0);
}
final int w = componentOld.getWidth();
final int h = componentOld.getHeight();
final Point p1 = componentOld.getLocation();
final Point p2 = new Point(0, 0);
if (slideType == LEFT) {
p2.x += w;
}
if (slideType == RIGHT) {
p2.x -= w;
}
if (slideType == TOP) {
p2.y += h;
}
if (slideType == BOTTOM) {
p2.y -= h;
}
componentNew.setLocation(p2);
int step = 0;
if ((slideType == LEFT) || (slideType == RIGHT)) {
step = (int) (((float) parent.getWidth() / (float) Toolkit.getDefaultToolkit().getScreenSize().width) * 40.f);
}
else {
step = (int) (((float) parent.getHeight() / (float) Toolkit.getDefaultToolkit().getScreenSize().height) * 20.f);
}
step = step < 5 ? 5 : step;
basePanel.add(componentNew);
basePanel.revalidate();
if (useLoop) {
final int max = (slideType == LEFT) || (slideType == RIGHT) ? w : h;
final long t0 = System.currentTimeMillis();
for (int i = 0; i != (max / step); i++) {
switch (slideType) {
case LEFT: {
p1.x -= step;
componentOld.setLocation(p1);
p2.x -= step;
componentNew.setLocation(p2);
break;
}
case RIGHT: {
p1.x += step;
componentOld.setLocation(p1);
p2.x += step;
componentNew.setLocation(p2);
break;
}
case TOP: {
p1.y -= step;
componentOld.setLocation(p1);
p2.y -= step;
componentNew.setLocation(p2);
break;
}
case BOTTOM: {
p1.y += step;
componentOld.setLocation(p1);
p2.y += step;
componentNew.setLocation(p2);
break;
}
default:
new RuntimeException("ProgramCheck").printStackTrace();
break;
}

try {
Thread.sleep(500 / (max / step));
} catch (final Exception e) {
e.printStackTrace();
}
}
final long t1 = System.currentTimeMillis();
}
componentOld.setLocation(-10000, -10000);
componentNew.setLocation(0, 0);
}
}
}

Slide JPanel Content in a JForm on Java

he slides a panel (with his content) to the left so the panel on the right replaces it with a smooth effect

You question mentions you want the panel to "slide", but the code looks like you are trying to get the panel to "shrink", so it is replaced by another panel.

Assuming you have two panels each with the same size, then you can "slide" one out of view while the other slides into view.

To do this you an use a panel with a GridLayout. This way each component will be the same size. Then you add the panel to a scrollpane without any scrollbars. The size of the scrollpane will need to be set to the size of the first compnoent. Then you can "slide" the two panels by changing the position of the viewport. So in your Timer you would have code something like:

JViewport viewport = scrollPane.getViewport();
Point position = viewport.getViewPosition();
position.x += 5;
viewport.setViewPosition( position );

You would then stop the Timer when the position is greater than the size of the component.

JPanel animation

So here is the finished class:
New animations can be added easily. And they also do not interfere with each other. So multiple states can be triggered simultaneously.

The StatusBar.java

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;

import javax.swing.JComponent;
import javax.swing.Timer;

public class StatusBar extends JComponent implements ActionListener{

int width;
int height;
boolean bLoad, bWarn, bConfirm, bError;
Timer timer;
Color bgColor;
int xPosLoad, alphaWarn, alphaConfirm, alphaError;
float cntWarn, cntConfirm, cntError;
int cntLoad;
final int barLength = 200;

public StatusBar(Color bg){
width = getWidth();
height = getHeight();
xPosLoad = -barLength;
alphaWarn = 0;
alphaConfirm = 0;
alphaError = 0;
bgColor = bg;

timer = new Timer(10, this);

this.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent event) {
width = getWidth();
height = getHeight();
}
});
}

@Override
protected void paintComponent(Graphics g) {
// Background
g.setColor(bgColor);
g.fillRect(0, 0, width, height);

// loading
Graphics2D g2d = (Graphics2D)g;
GradientPaint gp = new GradientPaint(xPosLoad,0, new Color(0,0,0,0), xPosLoad+barLength, 0, new Color(200, 200, 255));
g2d.setPaint(gp);
g2d.fillRect(xPosLoad, 0, barLength, height);

// Green
g.setColor(new Color(20, 210, 60, alphaConfirm));
g.fillRect(0, 0, width, height);

// Yellow
g.setColor(new Color(255, 223, 0, alphaWarn));
g.fillRect(0, 0, width, height);

// Red
g.setColor(new Color(255, 0, 0, alphaError));
g.fillRect(0, 0, width, height);

}

@Override
public void actionPerformed(ActionEvent e) {
// step increase for all active components
boolean toggle = false;
if (bConfirm){
if(cntConfirm < 1){
cntConfirm += 0.01f;
alphaConfirm = lerp(cntConfirm, 255, true);
}else{
bConfirm = false;
cntConfirm = 0;
alphaConfirm = 0;
}
toggle = true;
}
if (bWarn){
if(cntWarn < 1){
cntWarn += 0.01f;
alphaWarn = lerp(cntWarn, 255, true);
}else{
bWarn = false;
cntWarn = 0;
alphaWarn = 0;
}
toggle = true;
}
if (bError){
if(cntError < 1){
cntError += 0.01f;
alphaError = lerp(cntError, 255, true);
}else{
bError = false;
cntError = 0;
alphaError = 0;
}
toggle = true;
}
if (bLoad){
if(cntLoad < 100){
cntLoad += 1;
xPosLoad = (cntLoad * (width + barLength)) / 100 - barLength;
}else{
cntLoad = 0;
xPosLoad = -barLength;
}
toggle = true;
}

repaint();
if (!toggle){
timer.stop();
}
}

private void startTimer(){
if (!timer.isRunning())
timer.start();
}

public void setBG(Color bg){
bgColor = bg;
System.out.println("Color: " + bgColor);
repaint();
}

// Green flash
public void confirm(){
// set values
bConfirm = true;
alphaConfirm = 255;
cntConfirm = 0;

startTimer();
}

//Yellow flash
public void warning(){
// restart values
bWarn = true;
alphaWarn = 255;
cntWarn = 0;

startTimer();
}

//Red Flash
public void error(){
// restart values
bError = true;
alphaError = 255;
cntError = 0;

startTimer();
}

//Blue load
public void loadStart(){
// restart values
bLoad = true;
xPosLoad = -barLength;
cntLoad = 0;

startTimer();
}

public void loadEnd(){
bLoad = false;
xPosLoad = -barLength;
}

private int lerp(float progress, int max, boolean inverse){
float x = progress;

float x2 = (float) Math.pow(x, 4);
float g = x + (1 - x);
float y = (float) ((float) x2 / (float)(Math.pow(g, 4)));

y = Math.min(y, 1);
y = Math.max(y, 0);

int res = (int) (y * max);

if (inverse){
res = max - res;
}
return res;
}
}

And the Example.java

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Example extends JFrame{

public static void main(String[] args){
new Example("Stat Example");
}

public Example(String title){
super(title);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

StatusBar stat = new StatusBar(Color.black);
stat.setPreferredSize(new Dimension(0, 10));

JPanel panel = new JPanel();

JButton bConfirm = new JButton("Confirm");
JButton bWarn = new JButton("Warning");
JButton bErr = new JButton("Error");
JButton bLoadS = new JButton("Start Loading");
JButton bLoadE = new JButton("End Loading");
panel.add(bConfirm);
panel.add(bWarn);
panel.add(bErr);
panel.add(bLoadS);
panel.add(bLoadE);

this.getContentPane().add(stat, BorderLayout.CENTER);
this.getContentPane().add(panel, BorderLayout.SOUTH);

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

// Listener

bConfirm.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
stat.confirm();
}
});

bWarn.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
stat.warning();
}
});

bErr.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
stat.error();
}
});

bLoadS.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
stat.loadStart();
}
});

bLoadE.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
stat.loadEnd();
}
});

}
}

animating components in Jpanel

To get started, you'll probably need to research these terms:

  • Slide layout (thanks @Andrew Thompson)
  • setLocation
  • TimerTask (I think, or maybe just Timer)
  • clobbering graphics objects

Once you know about that stuff, you'll be in a position to make the design decisions that you're asking about.

It sounds like you'll have a child panel with a slide layout. Try not to use null layouts when possible.

You'll set its location or style per each tick in a timer task.

If you're setting a style, you'll need to clone your graphics object to avoid "clobbering" it - that is, work on a copy of the graphics object, so the original isn't changed or repainted accidentally while you're working on it.



Related Topics



Leave a reply



Submit