Hallo!
Ich hab mal eine Frage zum Animieren in Swing. Da hab ich ja mal versucht, Animationen auf JLayeredPanes durchzuführen. Eine Weiterentwicklung davon kann mehrere Animationen gleichzeitig darstellen und arbeitet mit JPanels. Jetzt habe ich jedoch das Problem, dass bei diesen Animationen das Programm zwar "irgendwas" tut, das Panel allerdings leer bleibt.
Ich bekomme durchaus die Ausgabe, dass sich die Komponenten an der richtigen Stelle befinden, aber auf dem Bildschirm tut sich halt nichts.
Es wäre super, wenn sich jemand mal den Code anschauen könnte und mich ggf. auf den Fehler hinweisen könnte!
[EDIT]Ich habe gerade mal das Layout gewechselt. Im null-Layout bleibt das Panel leer (obwohl ich auch damit schon erfolgreich animiert habe), im FlowLayout geht es. Nur zerstört der LayoutManager dan natürlich die Animation, weil die Komponenten dadurch andauernd wieder in ihre vom Layout vorgeschriebene Position geschoben werden, während die Animation läuft.[/EDIT]
Hier der Code:
Die aufrufende Methode:
componentLib.JAnimationPane.java
threads.DelayableComplexAnimation.java
PS: Nicht über das "Complex" in ComplexAnimation wundern. Diese Animationen sind ja nicht wirklich "complex", aber es gibt auch eine noch einfachere und irgendwie musste ich sie ja unterscheiden können...
LG renwal
Ich hab mal eine Frage zum Animieren in Swing. Da hab ich ja mal versucht, Animationen auf JLayeredPanes durchzuführen. Eine Weiterentwicklung davon kann mehrere Animationen gleichzeitig darstellen und arbeitet mit JPanels. Jetzt habe ich jedoch das Problem, dass bei diesen Animationen das Programm zwar "irgendwas" tut, das Panel allerdings leer bleibt.
Ich bekomme durchaus die Ausgabe, dass sich die Komponenten an der richtigen Stelle befinden, aber auf dem Bildschirm tut sich halt nichts.
Es wäre super, wenn sich jemand mal den Code anschauen könnte und mich ggf. auf den Fehler hinweisen könnte!
[EDIT]Ich habe gerade mal das Layout gewechselt. Im null-Layout bleibt das Panel leer (obwohl ich auch damit schon erfolgreich animiert habe), im FlowLayout geht es. Nur zerstört der LayoutManager dan natürlich die Animation, weil die Komponenten dadurch andauernd wieder in ihre vom Layout vorgeschriebene Position geschoben werden, während die Animation läuft.[/EDIT]
Hier der Code:
Die aufrufende Methode:
Java:
/**
* Läd im UI die nächste Frage
*/
private void showQuestion(boolean next)
{
btnQcOK.setEnabled(false);
[...]
// Neue Frage laden
currentQuestion = (Question) questionsToAsk.next();
// Komponenten generieren
if (currentQuestion instanceof LueckentextQuestion)
{
[...]
}
[...]
else if (currentQuestion instanceof MultipleChoiceQuestion)
{
new Thread()
{
@Override
public void run()
{
// Ermitteln, ob die neue MultipleChoice-Frage mehrere richtige Antwortmöglichkeiten hat
// (dann wird statt JRadioButton JCheckBox verwendet).
boolean multipleCorrectAnswers = ((MultipleChoiceQuestion) currentQuestion).hasMultipleCorrectAnswers();
MultipleChoiceAnswer[] answers = ((MultipleChoiceQuestion) currentQuestion).getAnswers();
Component[] components = new Component[answers.length];
for (int i = 0; i < components.length; i++)
{
// Komponenten generieren
components[i] = MultipleChoiceAnswerComponentCreator.create(answers[i], multipleCorrectAnswers);
// Komponenten in das Animations-Panel einfügen
questionAnimationPane.add(components[i]);
}
// Animationsabfolge erzeugen
// Gesamthöhe berechnen
int height = 0;
for (Component c : components)
{
height+=c.getHeight();
}
// Wenn für einen Abstand von 10px genug Platz ist, diesen verwenden,
// anderenfalls passenden Abstand berechnen. D.h. der Abstand wird nicht
// übermäßig groß, wenn nur wenige Komponenten vorhanden sind.
float abstand = (height < (questionAnimationPane.getHeight() - ((components.length + 2) * 10))) ? 10 : ((float)(questionAnimationPane.getHeight() - height) / (components.length + 2));
// Animationen berechnen
double bisher = 0;
for (Component c : components)
{
// Animation berechnen
Point2D startLocation = new Point2D.Double(-c.getWidth(), bisher+abstand);
Point2D endLocation = new Point2D.Double((double) questionAnimationPane.getHeight() / 2, startLocation.getY());
// Animation in die Warteschlange aufnehmen
questionAnimationPane.addAnimationToWaitlist(c, startLocation, endLocation, 750, 100);
// Den Ausgangswert aktualisieren
bisher = startLocation.getY() + c.getHeight();
}
// Animationsabfolge starten und auf deren Ende warten
questionAnimationPane.startAnimationWaitlist(true);
}
}.start();
}
}
componentLib.JAnimationPane.java
Java:
package componentLib;
import java.awt.Component;
import java.awt.geom.Point2D;
import java.beans.Beans;
import java.util.ArrayList;
import javax.swing.JPanel;
import threads.BlendAnimation;
import threads.ComplexAnimation;
import threads.DelayableComplexAnimation;
/**
* Subclass von JLayeredPane für vollständige Animationen. Es können im Gegensatz zur
* JAnimLayeredPane mehr als eine Komponente gleichkeitig sichtbar sein, sodass
* Komponenten über den Bildschirm bewegt werden können. Jede sich auf dem Container
* befindliche Komponente kann animiert werden, auch mehrere gleichzeitig.
* Es besteht auch die Möglichkeit, mehrere Komponenten gleichzeitig zu animieren,
* um z.B. Auswahlfelder mit je einer leichten Verzögerung zur vorherigen Komponente
* einblenden zu können.
*
* @author renwal
* @version 1.1.1
*
*/
public class JAnimationPane extends JPanel {
private ArrayList<DelayableComplexAnimation> animationQueue = new ArrayList<DelayableComplexAnimation>();
/**
*
*/
private static final long serialVersionUID = -2482635299189180067L;
public JAnimationPane()
{
super();
}
/**
* Fügt eine neue Komponente zur JAnimationPane hinzu
*
* @param component
* Die Komponente, die hinzugefügt werden soll
*
* @return Die hinzugefügte Komponente (Vorgabe aus der superclass)
*
*/
@Override
public Component add(Component component)
{
// Überschriebene superclass-Methode für das Hinzufügen von
// Komponenten, um dafür zu sorgen, dass Komponenten nach
// dem Hinzufügen immer invisible sind.
System.out.println("adding");
final Component returnComponent = super.add(component);
// Neue Komponenten nicht ausblenden, wenn der Visual Editor läuft,
// damit sie noch bearbeitet werden können
if (!Beans.isDesignTime())
component.setVisible(false);
return returnComponent;
}
public Component add(Component component, boolean visible)
{
// Überschriebene superclass-Methode für das Hinzufügen von
// Komponenten, um dafür zu sorgen, dass Komponenten nach
// dem Hinzufügen immer invisible sind.
final Component returnComponent = super.add(component);
// Neue Komponenten nicht ausblenden, wenn der Visual Editor läuft,
// damit sie noch bearbeitet werden können
if (!Beans.isDesignTime() && !visible)
component.setVisible(visible);
return returnComponent;
}
[...]
public void addAnimationToWaitlist(Component c, Point2D p1, Point2D p2, int time, int delay)
{
// Parameter auf Korrektheit prüfen
if (c == null)
throw new NullPointerException();
System.out.println(c.getParent());
if (!c.getParent().equals(this))
throw new IllegalStateException("Component is not a child of this component.");
// TODO Die Animation wird hier auch gestartet, wenn Start und Ziel gleich sind!!
// Neue zeitgesteuerte Animation erstellen
DelayableComplexAnimation anim = new DelayableComplexAnimation(c, p1, p2, time, delay);
// Objekt in die Warteliste einfügen
animationQueue.add(anim);
}
public void startAnimationWaitlist(boolean wait)
{
if (animationQueue.size() == 0)
throw new IllegalStateException("No animations in waitlist.");
if (wait)
{
// Wenn auf das Ende gewartet werden soll
processAnimationQueue();
}
else
{
new Thread()
{
@Override
public void run()
{
processAnimationQueue();
}
}.start();
}
}
private void processAnimationQueue()
{
for (DelayableComplexAnimation anim : animationQueue)
{
anim.start();
}
boolean running;
do
{
// Erst running zurücksetzen
running = false;
// Sobald ein Timer gefunden wird, der noch läuft, weiter prüfen
for (DelayableComplexAnimation anim : animationQueue)
{
if (anim.isRunning())
{
running = true;
break;
}
}
try
{
Thread.sleep(50);
}
catch (InterruptedException interrupt)
{
// TODO Auto-generated catch block
}
}while(running);
animationQueue.clear();
}
}
threads.DelayableComplexAnimation.java
Java:
package threads;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import javax.swing.Timer;
/**
* Ermöglicht die zeitgesteuerte Ausführung von Animation mehrerer Komponenten.
* ACHTUNG: Es handelt sich hier, mit geringfügigen Anpassungen für die TimerTask-Klasse,
* um denselben Code, den auch threads.ComplexAnimation.java enthält! IMMER BEIDE ÄNDERN!
*
* @author renwal
* @version 1.2.1
*/
public class DelayableComplexAnimation extends Timer implements ActionListener {
/**
*
*/
private static final long serialVersionUID = 9171502232401292487L;
// Anzahl an Millisekunden pro generiertem Bild (aktuell 40, d.h. 25fps)
private static final int MS_PER_FRAME = 40;
Component component;
int time;
int delay;
Point2D p1;
Point2D p2;
/**
*
* @param component
* Die zu animierende Komponente
* @param p1
* Der Zielpunkt der Animation
* @param p2
* Der Startpunkt der Animation
* @param time
* Die Animationsdauer in ms
* @param delay
* Die Verzögerung bis zum Start der Animation
*/
public DelayableComplexAnimation(Component component, Point2D p1, Point2D p2, int time, int delay)
{
// Neue Instanz der Animations-Klasse erzeugen
// TODO Erst null, dann this, denn this geht in super() nicht.
super(time, null);
setLogTimers(true); //DEBUG
addActionListener(this);
// Parameter auf Korrektheit prüfen
if (p1.equals(p2))
throw new IllegalArgumentException(
"Start and end point must not be at the same location.");
if (time == 0)
throw new IllegalArgumentException("Time must not be 0.");
// Parameter übernehmen
this.component = component;
this.time = time;
this.delay = delay;
this.p1 = p1;
this.p2 = p2;
}
@Override
public void start()
{
// Vor dem Start vorbereiten
setRepeats(false);
setCoalesce(false);
setInitialDelay(delay);
super.start();
}
@Override
public void actionPerformed(ActionEvent e)
{
new Thread()
{
@Override
public void run()
{
// Anzahl an Wiederholungen berechnen
double steps = (double) time / MS_PER_FRAME;
// x- und y-Schritt-Größen berechnen
double deltaX = (p2.getX()-p1.getX())/steps;
double deltaY = (p2.getY()-p1.getY())/steps;
// Komponente ggf. sichtbar machen
if(!component.isVisible())
component.setVisible(true);
// Animation der Komponente ausführen
for (int i=0; i<=steps; i++)
{
// Startzeit für Zeitausgleich speichern
final long timeNow = System.currentTimeMillis();
int x = (int) (p1.getX() + i * deltaX);
int y = (int) (p1.getY() + i * deltaY);
System.out.println("frame: x->" + x + " y->" + y + " (" + this.getName() + ")");
component.setLocation(x, y);
try
{
// Zeitausgleich berechnen (Die vorgegebene Anzahl an ms
// minus
// die für das Rendern benötigte Zeit)
final long substrSleepTime = System.currentTimeMillis()
- timeNow;
// Zeitausgleich, wenn die benötigte Zeit nicht länger ist
// als die Soll-Zeit, um Animationsgeschwindigkeit konstant
// zu halten.
if (substrSleepTime < MS_PER_FRAME)
Thread.sleep(MS_PER_FRAME - substrSleepTime);
}
catch (final InterruptedException interrupt)
{
break;
}
}
}
}.start();
}
}
PS: Nicht über das "Complex" in ComplexAnimation wundern. Diese Animationen sind ja nicht wirklich "complex", aber es gibt auch eine noch einfachere und irgendwie musste ich sie ja unterscheiden können...
LG renwal