[英]How do I get an instance of a class that extends a JPanel to display in another classes JPanel?
[英]How do I start an animation (move a circle from one point to another) in a JPanel from a class that extends thread?
我有一個名為Player的類,它擴展了線程,它的參數是一個JPanel
(稱為DrawPanel
)和坐標x,y; 在Player
構造函數中,我在面板上的x,y位置畫一個圓圈(我不知道它是多么正確)。
在Player
運行功能中,我想啟動一個動畫,將一個小紅圈從玩家坐標移動到另一個點。
喜歡這個動畫。
我怎樣才能做到這一點?
public class Player extends Thread {
private Graphics graphic;
private Graphics2D g2;
private int x;
private int y;
private DrawPanel panel;
public Player(int x, int y,DrawPanel panel)
{
this.x = x;
this.y = y;
this.panel = panel;
graphic = panel.getGraphics();
g2 = (Graphics2D) graphic;
g2.fillOval( column,row, 10, 10);
}
public void run()
{
//startAnimation(this.x,this.y,destination.x,destination.y)
}
}
我只想開始,動畫並不容易,好動畫很難。 有很多理論可以讓動畫“看起來很好”,我不會在這里介紹,有更好的人和資源。
我將要討論的是如何在基本級別上進行Swing中的“好”動畫。
第一個問題似乎是你沒有很好地理解繪畫在Swing中是如何工作的。 你應該首先閱讀Swing中的Swing和Painting中的 Performing Custom Painting
接下來,您似乎沒有意識到Swing實際上不是線程安全的(並且是單線程的)。 這意味着您永遠不應該在事件調度線程的上下文之外更新UI或UI所依賴的任何狀態。 有關更多詳細信息,請參閱Swing中的並發 。
解決此問題的最簡單方法是使用Swing Timer
,有關詳細信息,請參閱如何使用Swing Timers 。
現在,您可以簡單地運行一個Timer
並進行直線,線性進展,直到所有點都達到目標,但這並不總是最好的解決方案,因為它不能很好地擴展,並且在不同的PC上看起來會有所不同,基於那里個人能力。
在大多數情況下,基於持續時間的動畫會產生更好的結果。 這允許算法在PC無法跟上時“丟棄”幀。 它可以更好地(時間和距離)擴展,並且可以高度配置。
我喜歡生成可重復使用的代碼塊,所以我將從一個簡單的“基於持續時間的動畫引擎”開始......
// Self contained, duration based, animation engine...
public class AnimationEngine {
private Instant startTime;
private Duration duration;
private Timer timer;
private AnimationEngineListener listener;
public AnimationEngine(Duration duration) {
this.duration = duration;
}
public void start() {
if (timer != null) {
return;
}
startTime = null;
timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
tick();
}
});
timer.start();
}
public void stop() {
timer.stop();
timer = null;
startTime = null;
}
public void setListener(AnimationEngineListener listener) {
this.listener = listener;
}
public AnimationEngineListener getListener() {
return listener;
}
public Duration getDuration() {
return duration;
}
public double getRawProgress() {
if (startTime == null) {
return 0.0;
}
Duration duration = getDuration();
Duration runningTime = Duration.between(startTime, Instant.now());
double progress = (runningTime.toMillis() / (double) duration.toMillis());
return Math.min(1.0, Math.max(0.0, progress));
}
protected void tick() {
if (startTime == null) {
startTime = Instant.now();
}
double rawProgress = getRawProgress();
if (rawProgress >= 1.0) {
rawProgress = 1.0;
}
AnimationEngineListener listener = getListener();
if (listener != null) {
listener.animationEngineTicked(this, rawProgress);
}
// This is done so if you wish to expand the
// animation listener to include start/stop events
// this won't interfer with the tick event
if (rawProgress >= 1.0) {
rawProgress = 1.0;
stop();
}
}
public static interface AnimationEngineListener {
public void animationEngineTicked(AnimationEngine source, double progress);
}
}
它不是太復雜,它有一段duration
,它將運行。 將tick
在(不小於5毫秒)以規則的間隔,並且將生成tick
事件,報告了動畫的當前進展(如0之間的歸一化值和1)。
這里的想法是我們將“引擎”與正在使用它的那些元素分離。 這使我們能夠將它用於更廣泛的可能性。
接下來,我需要一些方法來跟蹤我移動物體的位置......
public class Ping {
private Point point;
private Point from;
private Point to;
private Color fillColor;
private Shape dot;
public Ping(Point from, Point to, Color fillColor) {
this.from = from;
this.to = to;
this.fillColor = fillColor;
point = new Point(from);
dot = new Ellipse2D.Double(0, 0, 6, 6);
}
public void paint(Container parent, Graphics2D g2d) {
Graphics2D copy = (Graphics2D) g2d.create();
int width = dot.getBounds().width / 2;
int height = dot.getBounds().height / 2;
copy.translate(point.x - width, point.y - height);
copy.setColor(fillColor);
copy.fill(dot);
copy.dispose();
}
public Rectangle getBounds() {
int width = dot.getBounds().width;
int height = dot.getBounds().height;
return new Rectangle(point, new Dimension(width, height));
}
public void update(double progress) {
int x = update(progress, from.x, to.x);
int y = update(progress, from.y, to.y);
point.x = x;
point.y = y;
}
protected int update(double progress, int from, int to) {
int distance = to - from;
int value = (int) Math.round((double) distance * progress);
value += from;
if (from < to) {
value = Math.max(from, Math.min(to, value));
} else {
value = Math.max(to, Math.min(from, value));
}
return value;
}
}
這是一個簡單的對象,它獲取起點和終點,然后根據進展計算這些點之間的對象位置。 它可以根據要求塗漆。
現在,我們只需要一些方法將它組合在一起......
public class TestPane extends JPanel {
private Point source;
private Shape sourceShape;
private List<Ping> pings;
private List<Shape> destinations;
private Color[] colors = new Color[]{Color.BLACK, Color.BLUE, Color.CYAN, Color.DARK_GRAY, Color.GREEN, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.RED, Color.YELLOW};
private AnimationEngine engine;
public TestPane() {
source = new Point(10, 10);
sourceShape = new Ellipse2D.Double(source.x - 5, source.y - 5, 10, 10);
Dimension size = getPreferredSize();
Random rnd = new Random();
int quantity = 1 + rnd.nextInt(10);
pings = new ArrayList<>(quantity);
destinations = new ArrayList<>(quantity);
for (int index = 0; index < quantity; index++) {
int x = 20 + rnd.nextInt(size.width - 25);
int y = 20 + rnd.nextInt(size.height - 25);
Point toPoint = new Point(x, y);
// Create the "ping"
Color color = colors[rnd.nextInt(colors.length)];
Ping ping = new Ping(source, toPoint, color);
pings.add(ping);
// Create the destination shape...
Rectangle bounds = ping.getBounds();
Shape destination = new Ellipse2D.Double(toPoint.x - (bounds.width / 2d), toPoint.y - (bounds.height / 2d), 10, 10);
destinations.add(destination);
}
engine = new AnimationEngine(Duration.ofSeconds(10));
engine.setListener(new AnimationEngine.AnimationEngineListener() {
@Override
public void animationEngineTicked(AnimationEngine source, double progress) {
for (Ping ping : pings) {
ping.update(progress);
}
repaint();
}
});
engine.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
// This is probably overkill, but it will make the output look nicer ;)
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
// Lines first, these could be cached
g2d.setColor(Color.LIGHT_GRAY);
double fromX = sourceShape.getBounds2D().getCenterX();
double fromY = sourceShape.getBounds2D().getCenterY();
for (Shape destination : destinations) {
double toX = destination.getBounds2D().getCenterX();
double toY = destination.getBounds2D().getCenterY();
g2d.draw(new Line2D.Double(fromX, fromY, toX, toY));
}
// Pings, so they appear above the line, but under the points
for (Ping ping : pings) {
ping.paint(this, g2d);
}
// Destination and source
g2d.setColor(Color.BLACK);
for (Shape destination : destinations) {
g2d.fill(destination);
}
g2d.fill(sourceShape);
g2d.dispose();
}
}
好吧,這看起來很“復雜”,但它非常簡單。
然后,動畫引擎將循環遍歷所有Ping
並根據當前進度值更新它們並觸發新的繪制過程,然后繪制源點和目標點之間的線,繪制Ping
,然后最后繪制源和所有目標點。 簡單。
啊,嗯,這要復雜得多,需要更復雜的動畫引擎。
一般來說,你可以建立一個“動畫”的概念。 然后,這將由一個連續“勾選”的中央“引擎”更新(本身並不局限於持續時間)。
然后,每個“動畫”都需要決定如何更新或報告其狀態並允許更新其他對象。
在這種情況下,我會尋求更現成的解決方案,例如......
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class JavaApplication124 {
public static void main(String[] args) {
new JavaApplication124();
}
public JavaApplication124() {
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 Point source;
private Shape sourceShape;
private List<Ping> pings;
private List<Shape> destinations;
private Color[] colors = new Color[]{Color.BLACK, Color.BLUE, Color.CYAN, Color.DARK_GRAY, Color.GREEN, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.RED, Color.YELLOW};
private AnimationEngine engine;
public TestPane() {
source = new Point(10, 10);
sourceShape = new Ellipse2D.Double(source.x - 5, source.y - 5, 10, 10);
Dimension size = getPreferredSize();
Random rnd = new Random();
int quantity = 1 + rnd.nextInt(10);
pings = new ArrayList<>(quantity);
destinations = new ArrayList<>(quantity);
for (int index = 0; index < quantity; index++) {
int x = 20 + rnd.nextInt(size.width - 25);
int y = 20 + rnd.nextInt(size.height - 25);
Point toPoint = new Point(x, y);
// Create the "ping"
Color color = colors[rnd.nextInt(colors.length)];
Ping ping = new Ping(source, toPoint, color);
pings.add(ping);
// Create the destination shape...
Rectangle bounds = ping.getBounds();
Shape destination = new Ellipse2D.Double(toPoint.x - (bounds.width / 2d), toPoint.y - (bounds.height / 2d), 10, 10);
destinations.add(destination);
}
engine = new AnimationEngine(Duration.ofSeconds(10));
engine.setListener(new AnimationEngine.AnimationEngineListener() {
@Override
public void animationEngineTicked(AnimationEngine source, double progress) {
for (Ping ping : pings) {
ping.update(progress);
}
repaint();
}
});
engine.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
// This is probably overkill, but it will make the output look nicer ;)
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
// Lines first, these could be cached
g2d.setColor(Color.LIGHT_GRAY);
double fromX = sourceShape.getBounds2D().getCenterX();
double fromY = sourceShape.getBounds2D().getCenterY();
for (Shape destination : destinations) {
double toX = destination.getBounds2D().getCenterX();
double toY = destination.getBounds2D().getCenterY();
g2d.draw(new Line2D.Double(fromX, fromY, toX, toY));
}
// Pings, so they appear above the line, but under the points
for (Ping ping : pings) {
ping.paint(this, g2d);
}
// Destination and source
g2d.setColor(Color.BLACK);
for (Shape destination : destinations) {
g2d.fill(destination);
}
g2d.fill(sourceShape);
g2d.dispose();
}
}
// Self contained, duration based, animation engine...
public static class AnimationEngine {
private Instant startTime;
private Duration duration;
private Timer timer;
private AnimationEngineListener listener;
public AnimationEngine(Duration duration) {
this.duration = duration;
}
public void start() {
if (timer != null) {
return;
}
startTime = null;
timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
tick();
}
});
timer.start();
}
public void stop() {
timer.stop();
timer = null;
startTime = null;
}
public void setListener(AnimationEngineListener listener) {
this.listener = listener;
}
public AnimationEngineListener getListener() {
return listener;
}
public Duration getDuration() {
return duration;
}
public double getRawProgress() {
if (startTime == null) {
return 0.0;
}
Duration duration = getDuration();
Duration runningTime = Duration.between(startTime, Instant.now());
double progress = (runningTime.toMillis() / (double) duration.toMillis());
return Math.min(1.0, Math.max(0.0, progress));
}
protected void tick() {
if (startTime == null) {
startTime = Instant.now();
}
double rawProgress = getRawProgress();
if (rawProgress >= 1.0) {
rawProgress = 1.0;
}
AnimationEngineListener listener = getListener();
if (listener != null) {
listener.animationEngineTicked(this, rawProgress);
}
// This is done so if you wish to expand the
// animation listener to include start/stop events
// this won't interfer with the tick event
if (rawProgress >= 1.0) {
rawProgress = 1.0;
stop();
}
}
public static interface AnimationEngineListener {
public void animationEngineTicked(AnimationEngine source, double progress);
}
}
public class Ping {
private Point point;
private Point from;
private Point to;
private Color fillColor;
private Shape dot;
public Ping(Point from, Point to, Color fillColor) {
this.from = from;
this.to = to;
this.fillColor = fillColor;
point = new Point(from);
dot = new Ellipse2D.Double(0, 0, 6, 6);
}
public void paint(Container parent, Graphics2D g2d) {
Graphics2D copy = (Graphics2D) g2d.create();
int width = dot.getBounds().width / 2;
int height = dot.getBounds().height / 2;
copy.translate(point.x - width, point.y - height);
copy.setColor(fillColor);
copy.fill(dot);
copy.dispose();
}
public Rectangle getBounds() {
int width = dot.getBounds().width;
int height = dot.getBounds().height;
return new Rectangle(point, new Dimension(width, height));
}
public void update(double progress) {
int x = update(progress, from.x, to.x);
int y = update(progress, from.y, to.y);
point.x = x;
point.y = y;
}
protected int update(double progress, int from, int to) {
int distance = to - from;
int value = (int) Math.round((double) distance * progress);
value += from;
if (from < to) {
value = Math.max(from, Math.min(to, value));
} else {
value = Math.max(to, Math.min(from, value));
}
return value;
}
}
}
正如我所說,好動畫,很難。 需要付出很多努力才能做好。 我甚至沒有談論緩和,鏈接或混合算法,所以當我說,相信我,這實際上是一個簡單,可重復使用的解決方案
不要相信我, 在Java Swing中查看JButton懸停動畫
試試這個:
public class Player extends Thread {
private Graphics graphic;
private Graphics2D g2;
private int x;
private int y;
private DrawPanel panel;
private numberOfIteration=5;
private currentNumberOfIteration=0;
public Player(int x, int y,DrawPanel panel)
{
this.x = x;
this.y = y;
this.panel = panel;
graphic = panel.getGraphics();
g2 = (Graphics2D) graphic;
g2.fillOval( column,row, 10, 10);
}
public Player(int x, int y,DrawPanel panel,int numberOfIteration)
{
this.x = x;
this.y = y;
this.panel = panel;
this.numberOfIteration=numberOfIterarion;
graphic = panel.getGraphics();
g2 = (Graphics2D) graphic;
g2.fillOval( column,row, 10, 10);
}
public void run()
{
//startAnimation(this.x,this.y,destination.x,destination.y)
currentNumberOfIteration=(++currentNumberOfIteration)%numberOfIteration;
currentX=(int)((destinationX*currentNumberOfIteration+this.x)/(currentNumberOfIteration+1));
currentY=(int)((destinationY*currentNumberOfIteration+this.y)/(currentNumberOfIteration+1));
g2.fillOval( currentX,currentY, 10, 10);
}
}
我沒有看到destinationX
和destinationY
任何聲明部分。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.