iam just starting programming in Java. Have issue with classes. As i try to use Windows in my project it keep shows this errors: Multiple markers at this line
- The constructor Window() is not visible
- The constructor Window(GameContainer) is
undefined
i tried make my class public but it didnt help. Can anyone helps me? Error is showing on Window lines in main class. Thanks anyone for helpss :)
main class:
package g_core_engine;
import java.awt.Window;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
public class GameContainer implements Runnable{
private Thread thread;
private Window window;
private boolean running = false;
private final double UPDATE_CAP = 1.0 / 60.0;
private int width = 320, height = 240;
private float Scale = 4;
private String title = "g_core_engine v1.0";
public GameContainer()
{
}
//Method that starts the actual computing of the game
public void start()
{
window = new Window();
thread = new Thread(this);
thread.run();
}
public void stop()
{
}
//Method that contains the update and render loops
public void run()
{
running = true;
boolean render = false;
enter code heredouble firstTime = 0;
double lastTime = System.nanoTime() / 1000000000.0;
double passedTime = 0;
double unprocessedTime = 0;
double frameTime = 0;
int frames = 0;
int fps = 0;
while(running)
{
render = false;
firstTime = System.nanoTime() / 1000000000.0;
passedTime = firstTime - lastTime;
lastTime = firstTime;
unprocessedTime += passedTime;
frameTime += passedTime;
while(unprocessedTime >= UPDATE_CAP)
{
unprocessedTime -= UPDATE_CAP;
render = true;
//TODO: Update Game
if(frameTime >= 1.0)
{
frameTime = 0;
fps = frames;
frames = 0;
System.out.println("FPS: "+fps);
}
}
if(render)
{
//TODO: Render Game
window.update();
frames++;
}
else
{
try
{
thread.sleep(1);
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
}
dispose();
}
public void dispose()
{
}
//Main method that runs on program start
public static void main(String[] args)
{
new GameContainer().start();
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getScale() {
// TODO Auto-generated method stub
return 0;
}
}
second class:
package g_core_engine;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
public class Window
{
private JFrame frame;
private BufferedImage image;
private Canvas canvas;
private BufferStrategy bs;
private Graphics g;
public Window (GameContainer gc)
{
image = new BufferedImage(gc.getWidth(), gc.getHeight(), BufferedImage.TYPE_INT_RGB);
canvas = new Canvas();
Dimension s = new Dimension((int)(gc.getWidth() * gc.getScale()), (int)(gc.getHeight() * gc.getScale()));
canvas.setPreferredSize(s);
canvas.setMaximumSize(s);
canvas.setMinimumSize(s);
frame = new JFrame(gc.getTitle());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(canvas, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
canvas.createBufferStrategy(2);
bs = canvas.getBufferStrategy();
g = bs.getDrawGraphics();
}
public void update()
{
g.drawImage(image, 0, 0, canvas.getWidth(), canvas.getHeight(), null);
bs.show();
}
}
Looks like you imported a Window class:
import java.awt.Window;
So when you're trying to make an instance of your "window" its actually trying to make a java.awt.Window. Be careful with class names. Change the name of your Window class (and then change that Window.java file of yours to the new name you pick / as well as anywhere else you were trying to use your Window) and it should work fine.
I have a program that does an animation using timers switching images. When the program is on its last image I use a class to create a buffered image of that image with text over it. When the last image of the animation is displayed I want to change the image displayed to the buffered image. I can't get it to work. The code as is plays as if the bolded section isnt there. If I delete the line above it, it displays the image with text over it and nothing else. What edits should I make to my code to fix this?
The Class that does the animation
**import java.awt.event.*;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Font;
import java.awt.image.*;
import java.io.*;
import java.io.File;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.net.URL;
import javax.swing.*;
import javax.swing.*;
import javax.imageio.ImageIO;
/**
* Write a description of class Reveal here.
*
* #author (your name)
* #version (a version number or a date)
*/
public class Reveal extends JPanel
{
private JPanel panel = new JPanel(); //a panel to house the label
private JLabel label = new JLabel(); //a label to house the image
private String[] image = {"Jack in the Box 1.png","Jack in the Box 2.png","Jack in the Box 3.png","Jack in the Box 4.png","Jack in the Box 5.png","Jack in the Box 6.png","Jack in the Box 7.png"}; //an array to hold the frames of the animation
private ImageIcon[] icon = new ImageIcon[7]; //an array of icons to be the images
private JFrame f;
private TextOverlay TO;
private Timer timer;
private Timer timer2;
int x = 0;
int y = 4;
int counter = 0;
/**
* Constructor for objects of class Reveal
*/
public Reveal(String name, int number)
{
TO = new TextOverlay("Jack in the Box 7.png", name, number);
for (int h = 0; h < 7; h++){
icon[h] = new ImageIcon(image[h]);
icon[h].getImage();
}
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
//Sets the size of the window
f.setSize(800,850);
panel = new JPanel();
label = new JLabel();
label.setIcon( icon[x] );
panel.add(label);
setVisible(true);
f.add(panel);
display(name, number);
**f.add(TO);**
}
public void display(String name, int number){
timer = new Timer(150, new ActionListener(){
public void actionPerformed(ActionEvent e) {
if (counter > 27){
timer.stop();
timer2.start(); //starts the second half of the animation
}else{
if (x != 3){
x++;
}else{
x = 0;
}
label.setIcon( icon[x] );
counter++;
} //ends if-else
} //ends action method
}); //ends timer
timer2 = new Timer(250, new ActionListener(){
public void actionPerformed(ActionEvent e){
if (y > 6) {
timer2.stop();
}else{
label.setIcon( icon[y] );
y++;
} //ends if-else
} //ends action method
}); //ends timer2
timer.start();
}
}
**
The class that puts text over an image
import java.io.*;
import java.awt.*;
import javax.swing.*;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
/**
* #see https://stackoverflow.com/questions/2658663
*/
public class TextOverlay extends JPanel {
private BufferedImage image;
private String name;
private String fileX;
private int number;
public TextOverlay(String f, String s, int n) {
name = s;
number = n;
fileX = f;
try {
image = ImageIO.read(new File(fileX));
} catch (IOException e) {
e.printStackTrace();
}
image = process(image, name, number);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
private BufferedImage process(BufferedImage old, String name, int number) {
int w = old.getWidth();
int h = old.getHeight();
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.drawImage(old, 0, 0, w, h, this);
g2d.setPaint(Color.black);
g2d.setFont(new Font("Franklin Gothic Demi Cond", Font.PLAIN, 30));
String s1 = name;
String s2 = Integer.toString(number);;
FontMetrics fm = g2d.getFontMetrics();
g2d.drawString(s1, 40, 90);
g2d.drawString(s2, 40, 140);
g2d.dispose();
return img;
}
}
So, you seem to have a misunderstanding of how Swing works, you might find How to Use Swing Timers and Concurrency in Swing of some assistance.
Basically, when you start a Timer, it doesn't block at this point until the timer ends (and even if it did, your wouldn't work the way you wanted it to). Instead, a new thread is created and after the specified period a request is placed on Event Dispatching Thread to execute the supplied Runnable.
This means that when you do something like...
f.add(panel);
display(name, number);
f.add(TO);
You are actually adding the TO component onto of the JLabel (because the frame is using a BorderLayout and the CENTRE position is the default position.
Instead, in your second timer completes, you need to remove the label and add the TO component...
timer2 = new Timer(250, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (y > 6) {
timer2.stop();
Container parent = label.getParent();
parent.remove(label);
parent.add(TO);
parent.revalidate();
} else {
label.setIcon(icon[y]);
y++;
} //ends if-else
} //ends action method
}); //ends timer2
Runnable Example...
import java.awt.event.*;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Font;
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.LineBorder;
public class Reveal extends JPanel {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
new Reveal("Test", 5);
}
});
}
private JPanel panel = new JPanel(); //a panel to house the label
private JLabel label = new JLabel(); //a label to house the image
private ImageIcon[] icon = new ImageIcon[7]; //an array of icons to be the images
private JFrame f;
private TextOverlay TO;
private Timer timer;
private Timer timer2;
int x = 0;
int y = 4;
int counter = 0;
/**
* Constructor for objects of class Reveal
*/
public Reveal(String name, int number) {
TO = new TextOverlay("Jack in the Box 7.png", name, number);
for (int h = 0; h < 7; h++) {
icon[h] = new ImageIcon(makeImage(h));
icon[h].getImage();
}
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
//Sets the size of the window
f.setSize(800, 850);
panel = new JPanel(new GridBagLayout());
label = new JLabel();
label.setIcon(icon[x]);
label.setBorder(new LineBorder(Color.RED));
panel.add(label);
f.add(panel);
display(name, number);
// f.add(TO);
setVisible(true);
}
public void display(String name, int number) {
timer = new Timer(150, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (counter > 27) {
timer.stop();
timer2.start(); //starts the second half of the animation
} else {
if (x != 3) {
x++;
} else {
x = 0;
}
label.setIcon(icon[x]);
counter++;
} //ends if-else
} //ends action method
}); //ends timer
timer2 = new Timer(250, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (y > 6) {
timer2.stop();
Container parent = label.getParent();
parent.remove(label);
parent.add(TO);
parent.revalidate();
} else {
label.setIcon(icon[y]);
y++;
} //ends if-else
} //ends action method
}); //ends timer2
timer.start();
}
protected BufferedImage makeImage(int h) {
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
FontMetrics fm = g2d.getFontMetrics();
String text = Integer.toString(h);
int x = (100 - fm.stringWidth(text)) / 2;
int y = ((100 - fm.getHeight()) / 2) + fm.getAscent();
g2d.setColor(Color.BLUE);
g2d.fillRect(0, 0, 100, 100);
g2d.setColor(Color.BLACK);
g2d.drawString(text, x, y);
g2d.dispose();
return img;
}
public class TextOverlay extends JPanel {
private BufferedImage image;
private String name;
private String fileX;
private int number;
public TextOverlay(String f, String s, int n) {
name = s;
number = n;
fileX = f;
image = makeImage(n);
image = process(image, name, number);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
private BufferedImage process(BufferedImage old, String name, int number) {
int w = old.getWidth();
int h = old.getHeight();
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.drawImage(old, 0, 0, w, h, this);
g2d.setPaint(Color.black);
g2d.setFont(new Font("Franklin Gothic Demi Cond", Font.PLAIN, 30));
String s1 = name;
String s2 = Integer.toString(number);;
FontMetrics fm = g2d.getFontMetrics();
g2d.drawString(s1, 40, 90);
g2d.drawString(s2, 40, 140);
g2d.dispose();
return img;
}
}
}
A "slightly" different approach...
Animation is actually a really complex subject which is not easy to implement well.
This is why, when faced with problems like these, I prefer to look at libraries which have already been implemented to help solve them. I'd recommend having a look at:
The Timing Framework
Trident
universal-tween-engine
as some starting points.
While I prefer to use libraries, sometimes it's not possible or the libraries don't fit my overall needs ... that and I like to dabble ... it's kind of a hobby.
Based on what I can understand from your code, you're trying to start out with a fast animation and then slow it down till you get to the last frame. In animation theory, this is commonly known as easement, more specifically, "slow/ease out".
The following borrows from a bunch of snippets I've been playing with (to devise a more reusable library) that will basically (randomly) display the images over a period of 4 seconds, with the animation slowing down and finally, presenting the "lucky" number
nb The gif animation is actually really slow, you'll need to run it to see the difference
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.swing.*;
public class Reveal extends JPanel {
public static void main(String[] args) {
new Reveal();
}
public Reveal() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private IntAnimatable animatable;
private List<ImageIcon> icons = new ArrayList<>(25);
private JLabel label = new JLabel();
public TestPane() {
setLayout(new GridBagLayout());
IntRange range = new IntRange(0, 111);
animatable = new IntAnimatable(range, Duration.ofSeconds(4), Easement.SLOWOUT, new AnimatableListener<Integer>() {
#Override
public void animationChanged(Animatable<Integer> animator) {
int value = animator.getValue();
int index = value % 7;
ImageIcon icon = icons.get(index);
if (label.getIcon() != icon) {
label.setIcon(icon);
}
}
}, new AnimatableLifeCycleAdapter<Integer>() {
#Override
public void animationCompleted(Animatable<Integer> animator) {
BufferedImage img = makeImage(3);
writeTextOverImage("Lucky number", img);
ImageIcon luckNumber = new ImageIcon(img);
label.setIcon(luckNumber);
}
});
for (int index = 0; index < 7; index++) {
icons.add(new ImageIcon(makeImage(index)));
}
Collections.shuffle(icons);
add(label);
Animator.INSTANCE.add(animatable);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
protected void writeTextOverImage(String text, BufferedImage img) {
Graphics2D g2d = img.createGraphics();
Font font = g2d.getFont();
font = font.deriveFont(Font.BOLD, font.getSize2D() + 2);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
int width = img.getWidth();
int height = img.getWidth();
int x = (width - fm.stringWidth(text)) / 2;
int y = fm.getAscent();
g2d.setColor(Color.YELLOW);
g2d.drawString(text, x, y);
g2d.dispose();
}
protected BufferedImage makeImage(int h) {
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
FontMetrics fm = g2d.getFontMetrics();
String text = Integer.toString(h);
int x = (100 - fm.stringWidth(text)) / 2;
int y = ((100 - fm.getHeight()) / 2) + fm.getAscent();
g2d.setColor(Color.BLUE);
g2d.fillRect(0, 0, 100, 100);
g2d.setColor(Color.WHITE);
g2d.drawString(text, x, y);
g2d.dispose();
return img;
}
/**** Range ****/
/*
A lot of animation is done from one point to another, this just
provides a self contained concept of a range which can be used to
calculate the value based on the current progression over time
*/
public abstract class Range<T> {
private T from;
private T to;
public Range(T from, T to) {
this.from = from;
this.to = to;
}
public T getFrom() {
return from;
}
public T getTo() {
return to;
}
#Override
public String toString() {
return "From " + getFrom() + " to " + getTo();
}
public abstract T valueAt(double progress);
}
public class IntRange extends Range<Integer> {
public IntRange(Integer from, Integer to) {
super(from, to);
}
public Integer getDistance() {
return getTo() - getFrom();
}
#Override
public Integer valueAt(double progress) {
int distance = getDistance();
int value = (int) Math.round((double) distance * progress);
value += getFrom();
return value;
}
}
/**** Animatable ****/
/*
The core concept of something that is animatable. This basic wraps up the
logic for calculating the progression of the animation over a period of time
and then use that to calculate the value of the range and then the observers
are notified so they can do stuff
*/
public class IntAnimatable extends AbstractAnimatableRange<Integer> {
public IntAnimatable(IntRange animationRange, Duration duration, Easement easement, AnimatableListener<Integer> listener, AnimatableLifeCycleListener<Integer> lifeCycleListener) {
super(animationRange, duration, easement, listener, lifeCycleListener);
}
}
public interface AnimatableListener<T> {
public void animationChanged(Animatable<T> animator);
}
public interface AnimatableLifeCycleListener<T> {
public void animationStopped(Animatable<T> animator);
public void animationCompleted(Animatable<T> animator);
public void animationStarted(Animatable<T> animator);
public void animationPaused(Animatable<T> animator);
}
public interface Animatable<T> {
public T getValue();
public void tick();
public Duration getDuration();
public Easement getEasement();
// Wondering if these should be part of a secondary interface
// Provide a "self managed" unit of work
public void start();
public void stop();
public void pause();
}
public class AnimatableLifeCycleAdapter<T> implements AnimatableLifeCycleListener<T> {
#Override
public void animationStopped(Animatable<T> animator) {
}
#Override
public void animationCompleted(Animatable<T> animator) {
}
#Override
public void animationStarted(Animatable<T> animator) {
}
#Override
public void animationPaused(Animatable<T> animator) {
}
}
public abstract class AbstractAnimatable<T> implements Animatable<T> {
private LocalDateTime startTime;
private Duration duration = Duration.ofSeconds(5);
private AnimatableListener<T> animatableListener;
private AnimatableLifeCycleListener<T> lifeCycleListener;
private Easement easement;
private double rawOffset;
public AbstractAnimatable(Duration duration, AnimatableListener<T> listener) {
this.animatableListener = listener;
this.duration = duration;
}
public AbstractAnimatable(Duration duration, AnimatableListener<T> listener, AnimatableLifeCycleListener<T> lifeCycleListener) {
this(duration, listener);
this.lifeCycleListener = lifeCycleListener;
}
public AbstractAnimatable(Duration duration, Easement easement, AnimatableListener<T> listener) {
this(duration, listener);
this.easement = easement;
}
public AbstractAnimatable(Duration duration, Easement easement, AnimatableListener<T> listener, AnimatableLifeCycleListener<T> lifeCycleListener) {
this(duration, easement, listener);
this.lifeCycleListener = lifeCycleListener;
}
public void setEasement(Easement easement) {
this.easement = easement;
}
#Override
public Easement getEasement() {
return easement;
}
public Duration getDuration() {
return duration;
}
protected void setDuration(Duration duration) {
this.duration = duration;
}
public double getCurrentProgress(double rawProgress) {
Easement easement = getEasement();
double progress = Math.min(1.0, Math.max(0.0, getRawProgress()));
if (easement != null) {
progress = easement.interpolate(progress);
}
return Math.min(1.0, Math.max(0.0, progress));
}
public double getRawProgress() {
if (startTime == null) {
return 0.0;
}
Duration duration = getDuration();
Duration runningTime = Duration.between(startTime, LocalDateTime.now());
double progress = rawOffset + (runningTime.toMillis() / (double) duration.toMillis());
return Math.min(1.0, Math.max(0.0, progress));
}
#Override
public void tick() {
if (startTime == null) {
startTime = LocalDateTime.now();
fireAnimationStarted();
}
double rawProgress = getRawProgress();
double progress = getCurrentProgress(rawProgress);
if (rawProgress >= 1.0) {
progress = 1.0;
}
tick(progress);
fireAnimationChanged();
if (rawProgress >= 1.0) {
fireAnimationCompleted();
}
}
protected abstract void tick(double progress);
#Override
public void start() {
if (startTime != null) {
// Restart?
return;
}
Animator.INSTANCE.add(this);
}
#Override
public void stop() {
stopWithNotitifcation(true);
}
#Override
public void pause() {
rawOffset += getRawProgress();
stopWithNotitifcation(false);
double remainingProgress = 1.0 - rawOffset;
Duration remainingTime = getDuration().minusMillis((long) remainingProgress);
setDuration(remainingTime);
lifeCycleListener.animationStopped(this);
}
protected void fireAnimationChanged() {
if (animatableListener == null) {
return;
}
animatableListener.animationChanged(this);
}
protected void fireAnimationCompleted() {
stopWithNotitifcation(false);
if (lifeCycleListener == null) {
return;
}
lifeCycleListener.animationCompleted(this);
}
protected void fireAnimationStarted() {
if (lifeCycleListener == null) {
return;
}
lifeCycleListener.animationStarted(this);
}
protected void fireAnimationPaused() {
if (lifeCycleListener == null) {
return;
}
lifeCycleListener.animationPaused(this);
}
protected void stopWithNotitifcation(boolean notify) {
Animator.INSTANCE.remove(this);
startTime = null;
if (notify) {
if (lifeCycleListener == null) {
return;
}
lifeCycleListener.animationStopped(this);
}
}
}
public abstract class AbstractAnimatableRange<T> extends AbstractAnimatable<T> {
private Range<T> range;
private T value;
public AbstractAnimatableRange(Range<T> range, Duration duration, AnimatableListener<T> listener) {
super(duration, listener);
this.range = range;
}
public AbstractAnimatableRange(Range<T> range, Duration duration, AnimatableListener<T> listener, AnimatableLifeCycleListener<T> lifeCycleListener) {
super(duration, listener, lifeCycleListener);
this.range = range;
}
public AbstractAnimatableRange(Range<T> range, Duration duration, Easement easement, AnimatableListener<T> listener) {
super(duration, easement, listener);
this.range = range;
}
public AbstractAnimatableRange(Range<T> range, Duration duration, Easement easement, AnimatableListener<T> listener, AnimatableLifeCycleListener<T> lifeCycleListener) {
super(duration, easement, listener, lifeCycleListener);
this.range = range;
}
protected void tick(double progress) {
setValue(range.valueAt(progress));
}
protected void setValue(T value) {
this.value = value;
}
#Override
public T getValue() {
return value;
}
}
/*
Easement, complicated, but fun
*/
public enum Easement {
SLOWINSLOWOUT(1d, 0d, 0d, 1d), FASTINSLOWOUT(0d, 0d, 1d, 1d), SLOWINFASTOUT(0d, 1d, 0d, 0d), SLOWIN(1d, 0d, 1d, 1d), SLOWOUT(0d, 0d, 0d, 1d);
private final double[] points;
private final List<PointUnit> normalisedCurve;
private Easement(double x1, double y1, double x2, double y2) {
points = new double[]{x1, y1, x2, y2};
final List<Double> baseLengths = new ArrayList<>();
double prevX = 0;
double prevY = 0;
double cumulativeLength = 0;
for (double t = 0; t <= 1; t += 0.01) {
Point2D xy = getXY(t);
double length = cumulativeLength + Math.sqrt((xy.getX() - prevX) * (xy.getX() - prevX) + (xy.getY() - prevY) * (xy.getY() - prevY));
baseLengths.add(length);
cumulativeLength = length;
prevX = xy.getX();
prevY = xy.getY();
}
normalisedCurve = new ArrayList<>(baseLengths.size());
int index = 0;
for (double t = 0; t <= 1; t += 0.01) {
double length = baseLengths.get(index++);
double normalLength = length / cumulativeLength;
normalisedCurve.add(new PointUnit(t, normalLength));
}
}
public double interpolate(double fraction) {
int low = 1;
int high = normalisedCurve.size() - 1;
int mid = 0;
while (low <= high) {
mid = (low + high) / 2;
if (fraction > normalisedCurve.get(mid).getPoint()) {
low = mid + 1;
} else if (mid > 0 && fraction < normalisedCurve.get(mid - 1).getPoint()) {
high = mid - 1;
} else {
break;
}
}
/*
* The answer lies between the "mid" item and its predecessor.
*/
final PointUnit prevItem = normalisedCurve.get(mid - 1);
final double prevFraction = prevItem.getPoint();
final double prevT = prevItem.getDistance();
final PointUnit item = normalisedCurve.get(mid);
final double proportion = (fraction - prevFraction) / (item.getPoint() - prevFraction);
final double interpolatedT = prevT + (proportion * (item.getDistance() - prevT));
return getY(interpolatedT);
}
protected Point2D getXY(double t) {
final double invT = 1 - t;
final double b1 = 3 * t * invT * invT;
final double b2 = 3 * t * t * invT;
final double b3 = t * t * t;
final Point2D xy = new Point2D.Double((b1 * points[0]) + (b2 * points[2]) + b3, (b1 * points[1]) + (b2 * points[3]) + b3);
return xy;
}
protected double getY(double t) {
final double invT = 1 - t;
final double b1 = 3 * t * invT * invT;
final double b2 = 3 * t * t * invT;
final double b3 = t * t * t;
return (b1 * points[2]) + (b2 * points[3]) + b3;
}
protected class PointUnit {
private final double distance;
private final double point;
public PointUnit(double distance, double point) {
this.distance = distance;
this.point = point;
}
public double getDistance() {
return distance;
}
public double getPoint() {
return point;
}
}
}
/**** Core Animation Engine ****/
public enum Animator {
INSTANCE;
private Timer timer;
private List<Animatable> properies;
private Animator() {
properies = new ArrayList<>(5);
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
List<Animatable> copy = new ArrayList<>(properies);
Iterator<Animatable> it = copy.iterator();
while (it.hasNext()) {
Animatable ap = it.next();
ap.tick();
}
if (properies.isEmpty()) {
timer.stop();
}
}
});
}
public void add(Animatable ap) {
properies.add(ap);
timer.start();
}
protected void removeAll(List<Animatable> completed) {
properies.removeAll(completed);
}
public void remove(Animatable ap) {
properies.remove(ap);
if (properies.isEmpty()) {
timer.stop();
}
}
}
}
I am making an animation by drawing different images dozens of milliseconds after the former one. something like this:
drawimage(image1,0,0,null);
try {
Thread.sleep(100);
}catch(Exception e){//To do something
}
drawimage(image2,0,0,null);
But the first image doesn't show until the second one appear. That means they appear at the same time.
My question is why does this happen?
Me: Where exactly is this code? Is it in a paint/paintComponent method?
OP: it is in paintComponent. I use it to make animation, but I am not sure it is a good way.
You're right it isn't a good way. Don't ever call Thread.sleep in the paintComponent method. I would avoid the Thread.sleep all together and use a javax.swing.Timer. See more How to Use Swing Timers
See examples here and here and here and here.
You could...
Use a list of Images and every iteration firing of the Timer event, add another Image to the List<Image> and call repaint()
You could...
Have an MyImage object class that has an Image field and a boolean draw field. In the Timer, loop through the MyImage objects and do something like
for (MyImage image: images) {
if (!image.isDraw()) {
image.setDraw(true);
break;
}
}
repaint();
For the MyImage List just loop through them in the paintComponent method and call it drawImage method, that you create.
Run this exmaple, showing the first option
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class AnimateImages extends JPanel {
private static final int IMAGE_ROWS = 10;
private static final int IMAGE_COLS = 10;
private static final int IMAGE_SIZE = 50;
private static final int DIM_WIDTH = IMAGE_COLS * IMAGE_SIZE;
private final List<MyImage> images;
private Image image;
private int currX = -IMAGE_SIZE;
private int currY;
public AnimateImages() {
try {
image = ImageIO.read(new URL("http://swoo.co.uk/content/images/icons/stackoverflow.png"));
} catch (IOException ex) {
Logger.getLogger(AnimateImages.class.getName()).log(Level.SEVERE, null, ex);
}
images = createImages();
Timer timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (MyImage image : images) {
if (!image.isDraw()) {
image.setDraw(true);
break;
}
repaint();
}
}
});
timer.start();
}
private List<MyImage> createImages() {
List<MyImage> list = new ArrayList<>();
for (int i = 0; i < IMAGE_ROWS * IMAGE_COLS; i++) {
if (currX >= DIM_WIDTH) {
currX = 0;
currY += IMAGE_SIZE;
} else {
currX += IMAGE_SIZE;
}
list.add(new MyImage(image, currX, currY));
}
return list;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (MyImage img : images) {
img.draw(g);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(IMAGE_SIZE * IMAGE_COLS, IMAGE_SIZE * IMAGE_ROWS);
}
public class MyImage {
Image image;
int x, y;
boolean draw = false;
public MyImage(Image image, int x, int y) {
this.image = image;
this.x = x;
this.y = y;
}
public void setDraw(boolean draw) {
this.draw = draw;
}
public boolean isDraw() {
return draw;
}
public void draw(Graphics g) {
if (draw) {
g.drawImage(image, x, y, IMAGE_SIZE, IMAGE_SIZE, AnimateImages.this);
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new AnimateImages());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
The program, in its current state, draws two images at the same time because there are two draw methods in the loop (if you are indeed using a loop).
In order to solve this issue, you should use only a single draw method and leave the delay in the same place. However, in order to cycle through all of the different image variables (if you have them named as numbers 1,2,3,4...etc in an array) you can use a for loop to draw them:
for (int i = 0; i< *however many images you have*; i++){
drawimage(image[i],0,0,null);
try {
Thread.sleep(100);
}catch(Exception e){//To do something
}
}
Edit
You do not use delays inside the paintComponent. Since you are, that is what is likely causing the issues. Move the delay into the main method of your program.
Your answer
You're getting this result because it is printing image1, waiting .1 seconds and then printing image2. Then it doesn't wait at all, the game is immediately updated and then prints image1. Your display time for image2 is microscopic and you're likely only seeing image1 as a result.
Furthermore, the images are never deleted or replaced and so you are constantly drawing images on top of each other which is causing a memory leak, and is likely the cause of you seeing both images.
Considerations
This is a handy little animation class that I got, and still use, from this tutorial. This class is pretty handy and you can add the animated sprites necessary for each animation.
With this class, when you do a drawImage() you can use object.getImage() to extract the current frame of the animation.
Just make sure that you're calling the animation's update() method in your game's main loop so that the animations are constantly being updated.
import java.awt.Image;
import java.util.ArrayList;
public class Animation {
private ArrayList frames;
private int currentFrame;
private long animTime;
private long totalDuration;
public Animation() {
frames = new ArrayList();
totalDuration = 0;
synchronized (this) {
animTime = 0;
currentFrame = 0;
}
}
public synchronized void addFrame(Image image, long duration) {
totalDuration += duration;
frames.add(new AnimFrame(image, totalDuration));
}
public synchronized void update(long elapsedTime) {
if (frames.size() > 1) {
animTime += elapsedTime;
if (animTime >= totalDuration) {
animTime = animTime % totalDuration;
currentFrame = 0;
}
while (animTime > getFrame(currentFrame).endTime) {
currentFrame++;
}
}
}
public synchronized Image getImage() {
if (frames.size() == 0) {
return null;
} else {
return getFrame(currentFrame).image;
}
}
private AnimFrame getFrame(int i) {
return (AnimFrame) frames.get(i);
}
private class AnimFrame {
Image image;
long endTime;
public AnimFrame(Image image, long endTime) {
this.image = image;
this.endTime = endTime;
}
}
}
I have created a simple musical metronome in Java. It starts and stops by pressing a button that has the shape of a butterfly. I would love to add a visual effect of the tempo by making the button/butterfly appear and disappear together with the metronome beat.
Would java.util.Timer be the way to go? Would that work with an image that is a button and that needs to keep its functions while pulsing?
Thank you so very much for suggestions and congratulations on the community.
Would java.util.Timer be the way to go?
Yes, this could easily be used to display a pulsating image. When I've done this before, I've created an array of sine wave constants in my constructor and have used them to set an alpha composite inside of the timer.
Would that work with an image that is a button and that needs to keep its functions while pulsing?
It's possible although a little more difficult since the button doesn't really render itself, but rather its componentUI, here one of the subclasses of the BasicButtonUI.
Well, I can do it without messing with the componentUI, but I'm not sure if this is the correct way:
import java.awt.AlphaComposite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
#SuppressWarnings("serial")
public class PulsingButton extends JPanel {
private static final int MAX_ALPHAS = 60;
private float alpha = 1.0f;
private JSpinner beatsPerMinSpinner = new JSpinner(new SpinnerNumberModel(60, 30, 120, 1));
private JButton button = new JButton("Button") {
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setComposite(((AlphaComposite)g2.getComposite()).derive(alpha));
super.paintComponent(g2);
};
};
private float[] alphas = new float[MAX_ALPHAS];
private Timer timer;
public PulsingButton() {
beatsPerMinSpinner.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
int value = ((Integer) beatsPerMinSpinner.getValue()).intValue();
setTimerDelay(value);
}
});
add(new JLabel("Beats Per Minute:"));
add(beatsPerMinSpinner);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Hello!");
}
});
add(button);
for (int i = 0; i < alphas.length; i++) {
double theta = (Math.PI * 2 * i) / alphas.length;
alphas[i] = (float) ((Math.cos(theta) + 1) / 2.0);
}
int bpm = ((Integer) beatsPerMinSpinner.getValue()).intValue();
timer = new Timer(setTimerDelay(bpm), new TimerListener());
timer.start();
System.out.println(setTimerDelay(bpm) + "");
}
private int setTimerDelay(int bpm) {
int milisecondsInMinute = 60 * 1000;
int delay = milisecondsInMinute / (bpm * alphas.length);
if (timer != null) {
timer.setDelay(delay);
}
return delay;
}
private class TimerListener implements ActionListener {
int index = 0;
#Override
public void actionPerformed(ActionEvent arg0) {
alpha = alphas[index];
index++;
index %= alphas.length;
repaint();
}
}
private static void createAndShowGui() {
PulsingButton mainPanel = new PulsingButton();
JFrame frame = new JFrame("PulsingButton");
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();
}
});
}
}
I need a scroll pane with scrollbars only visible during scroll events or when needed to pull.
Something that looks like the scroll bars on the nowadays smartphones.
I searched a lot but found only realizations in javascript.
So I tried it by myself.
Has anyone a better solution or hints?
The animation classes can be found here: TimingFrameWork
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.plaf.ScrollBarUI;
import javax.swing.plaf.basic.BasicScrollBarUI;
import org.jdesktop.animation.timing.Animator;
import org.jdesktop.animation.timing.interpolation.KeyFrames;
import org.jdesktop.animation.timing.interpolation.KeyTimes;
import org.jdesktop.animation.timing.interpolation.KeyValues;
import org.jdesktop.animation.timing.interpolation.PropertySetter;
/**
* A scrollpane like component, where the scroll bars are floating over the
* scrollable view to indicate the current scroll positions.
* The scroll indicators appear smoothly during scroll events and disappear
* smoothly afterwards.
* <p>
* The scrollbars can be dragged just as normal.</p>
* <p>
* The usage is similar to a classic scrollpane.</p>
*
* #author Jolly Littlebottom
*/
public class JScrollIndicator extends JLayeredPane {
private static final Color THUMB_COLOR = Color.DARK_GRAY;
private static final Color THUMB_BORDER_COLOR = Color.LIGHT_GRAY;
private static final float MAX_ALPHA = 0.8f;
private static final int THUMB_THICKNESS = 7;
private static final int THUMB_MIN_SIZE = 48;
private static final int THUMB_MARGIN = 3;
private static final int FADE_IN_TIME = 300;
private static final int STAY_TIME = 2000;
private static final int FADE_OUT_TIME = 1000;
private final JScrollPane scrollPane;
private final ControlPanel controlPanel;
/**
* Creates a <code>JScrollIndicator</code> that displays the contents of the
* specified component, where both horizontal and vertical scrollbars appear
* whenever the component's contents are larger than the view and scrolling
* in underway or the mouse is over the scrollbar position.
*
* #see #setViewportView
* #param view the component to display in the scrollpane's viewport
*/
public JScrollIndicator(final JComponent view) {
this(view, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
}
/**
* Creates a <code>JScrollIndicator</code> that displays the view component
* in a viewport whose view position can be controlled with a pair of
* scrollbars.
* The scrollbar policies specify when the scrollbars are displayed,
* For example, if <code>vsbPolicy</code> is
* <code>JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED</code>
* then the vertical scrollbar only appears if the view doesn't fit
* vertically. The available policy settings are listed at
* {#link #JScrollPane.setVerticalScrollBarPolicy} and
* {#link #JScrollPane.setHorizontalScrollBarPolicy}.
*
* #param view the component to display in the scrollpanes viewport
* #param vsbPolicy an integer that specifies the vertical scrollbar policy
* #param hsbPolicy an integer that specifies the horizontal scrollbar policy
*/
public JScrollIndicator(final JComponent view, int vsbPolicy, int hsbPolicy) {
scrollPane = new JScrollPane(view, vsbPolicy, hsbPolicy);
scrollPane.setBorder(BorderFactory.createEmptyBorder());
add(scrollPane, JLayeredPane.DEFAULT_LAYER);
controlPanel = new ControlPanel(scrollPane);
add(controlPanel, JLayeredPane.PALETTE_LAYER);
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
// listen to changes of JLayeredPane size
scrollPane.setSize(getSize());
scrollPane.getViewport().revalidate();
controlPanel.setSize(getSize());
controlPanel.revalidate();
}
});
}
/**
* Returns the scroll pane used by this scroll indicator.
* Use carefully (e.g. to set unit increments) because not all changes have an
* effect. You have to write listeners in this cases (e.g. for changing the
* scrollbar policy)
*
* #return
*/
public JScrollPane getScrollPane() {
return scrollPane;
}
private class ControlPanel extends JPanel {
private final JMyScrollBar vScrollBar;
private final JMyScrollBar hScrollBar;
private ControlPanel(JScrollPane scrollPane) {
setLayout(new BorderLayout());
setOpaque(false);
vScrollBar = new JMyScrollBar(JScrollBar.VERTICAL);
scrollPane.setVerticalScrollBar(vScrollBar);
scrollPane.remove(vScrollBar);
if (scrollPane.getVerticalScrollBarPolicy() != JScrollPane.VERTICAL_SCROLLBAR_NEVER) {
add(vScrollBar, BorderLayout.EAST);
}
hScrollBar = new JMyScrollBar(JScrollBar.HORIZONTAL);
scrollPane.setHorizontalScrollBar(hScrollBar);
scrollPane.remove(hScrollBar);
if (scrollPane.getHorizontalScrollBarPolicy() != JScrollPane.HORIZONTAL_SCROLLBAR_NEVER) {
add(hScrollBar, BorderLayout.SOUTH);
}
}
}
private class JMyScrollBar extends JScrollBar {
protected final MyScrollBarUI scrollUI;
public JMyScrollBar(int direction) {
super(direction);
scrollUI = new MyScrollBarUI(this);
super.setUI(scrollUI);
int size = THUMB_THICKNESS + THUMB_MARGIN;
setPreferredSize(new Dimension(size, size));
addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
scrollUI.setVisible(true, true);
}
#Override
public void mouseExited(MouseEvent e) {
scrollUI.setVisible(false, false);
}
});
addAdjustmentListener(new AdjustmentListener() {
#Override
public void adjustmentValueChanged(AdjustmentEvent e) {
scrollUI.setVisible(true, false);
}
});
}
#Override
public void setUI(ScrollBarUI ui) {
}
#Override
public void updateUI() {
}
#Override
public void paint(Graphics g) {
scrollUI.paintThumb(g, this); // just the thumb
}
#Override
public void repaint(Rectangle r) {
JScrollIndicator scrollIndicator = JScrollIndicator.this;
Rectangle rect = SwingUtilities.convertRectangle(this, r, scrollIndicator);
rect.grow(1, 1);
// ensure for a translucent thumb, that the view is first painted
scrollIndicator.repaint(rect);
}
}
public class MyScrollBarUI extends BasicScrollBarUI {
private JMyScrollBar scrollBar;
private float alpha = 0.0f;
private FadeAnimation fadeAnimation;
private MyScrollBarUI(JMyScrollBar scrollBar) {
this.scrollBar = scrollBar;
fadeAnimation = new FadeAnimation(this);
}
#Override
protected void installComponents() {
incrButton = new JButton();
decrButton = new JButton();
if (scrollBar.getOrientation() == JScrollBar.HORIZONTAL) {
int size = THUMB_THICKNESS + THUMB_MARGIN; // let lower right corner empty
incrButton.setPreferredSize(new Dimension(size, size));
}
else {
incrButton.setPreferredSize(new Dimension(THUMB_MARGIN, THUMB_MARGIN));
}
decrButton.setPreferredSize(new Dimension(THUMB_MARGIN, THUMB_MARGIN));
}
#Override
protected void installDefaults() {
super.installDefaults();
// ensure the minimum size of the thumb
int w = minimumThumbSize.width;
int h = minimumThumbSize.height;
if (scrollBar.getOrientation() == JScrollBar.VERTICAL) {
h = Math.max(h, Math.min(maximumThumbSize.height, THUMB_MIN_SIZE));
}
else {
w = Math.max(w, Math.min(maximumThumbSize.width, THUMB_MIN_SIZE));
}
minimumThumbSize = new Dimension(w, h);
}
private void paintThumb(Graphics g, JComponent c) {
if (alpha == 0.0f) {
return; // don't paint anything
}
g.setColor(getAlphaColor(THUMB_COLOR));
int radius = THUMB_THICKNESS >>> 1; // half width
Rectangle thumbBounds = getThumbBounds();
int x = thumbBounds.x;
int y = thumbBounds.y;
int w = thumbBounds.width;
int h = thumbBounds.height;
if (scrollBar.getOrientation() == JScrollBar.VERTICAL) {
w -= THUMB_MARGIN;
}
else {
h -= THUMB_MARGIN;
}
g.fillRoundRect(x, y, w, h, radius, radius);
g.setColor(getAlphaColor(THUMB_BORDER_COLOR));
g.drawRoundRect(x, y, w, h, radius, radius);
}
private Color getAlphaColor(Color color) {
if (alpha == 1.0f) {
return color;
}
int rgb = color.getRGB() & 0xFFFFFF; // color without alpha values
rgb |= ((int)(alpha*255)) << 24; // add alpha value
return new Color(rgb, true);
}
public void setAlpha(float alpha) {
this.alpha = alpha;
scrollBar.repaint(getThumbBounds());
}
public void setVisible(boolean visible, boolean mouseOver) {
if (visible) {
fadeAnimation.fadeIn(mouseOver);
}
else {
fadeAnimation.fadeOut();
}
scrollBar.repaint(getThumbBounds());
}
}
private class FadeAnimation {
private final MyScrollBarUI scrollUI;
private Animator fadeAnimator;
private Timer fadeOutTimer;
private boolean isFadeIn;
private FadeAnimation(MyScrollBarUI scrollUI) {
this.scrollUI = scrollUI;
}
public synchronized void fadeIn(boolean mouseOver) {
if (mouseOver) {
cancelTimer();
}
if (!isFadeIn) {
isFadeIn = true;
cancelAnimationAndTimer();
fadeAnimator = PropertySetter.createAnimator(
FADE_IN_TIME, scrollUI, "alpha",
new KeyFrames(KeyValues.create(scrollUI.alpha, MAX_ALPHA),
new KeyTimes(0.0f, 1.0f)));
fadeAnimator.start();
if (!mouseOver) {
fadeOutTimer = new Timer(FADE_IN_TIME + STAY_TIME,
new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
FadeAnimation.this.fadeOut();
}
});
fadeOutTimer.start();
}
}
}
public synchronized void fadeOut() {
cancelAnimationAndTimer();
if (isFadeIn) {
isFadeIn = false;
fadeAnimator = PropertySetter.createAnimator(
FADE_OUT_TIME, scrollUI, "alpha",
new KeyFrames(KeyValues.create(scrollUI.alpha, 0.0f),
new KeyTimes(0.0f, 1.0f)));
fadeAnimator.start();
}
}
private void cancelAnimationAndTimer() {
if (fadeAnimator != null && fadeAnimator.isRunning()) {
fadeAnimator.cancel();
fadeAnimator = null;
}
cancelTimer();
}
private void cancelTimer() {
if (fadeOutTimer != null) {
fadeOutTimer.stop();
fadeOutTimer = null;
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String text = "";
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 100; j++) {
text += i + " - " + j;
}
text += "\n";
}
JTextArea area = new JTextArea(text);
JScrollIndicator scrollIndicator = new JScrollIndicator(area);
frame.getContentPane().add(scrollIndicator);
frame.setBounds(100, 100, 200, 300);
frame.setVisible(true);
}
});
}
}