動畫按鈕動作實際上不是最難的問題,最難的問題是試圖移動數據約在客場中,你可以管理它,以及如何將源組件與目標連接...
首先,您需要一種方法,通過該方法可以跨組件邊界移動組件。雖然有可能是幾個方法可以做到這一點,最簡單的就是可能使用幀
public class AnimationPane extends JPanel {
public AnimationPane() {
setOpaque(false);
setLayout(null);
}
}
它沒有什麼特別的玻璃面板,它只是一個JPanel
這是透明的,沒有佈局管理器,通常情況下,不推薦使用,但在情況下,我們要採取控制..
現在,我們需要一些方法來製作動畫的運動...
public enum Animator {
INSTANCE;
private List<IAnimatable> animatables;
private Timer timer;
private Animator() {
animatables = new ArrayList<>(25);
timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
IAnimatable[] anins = animatables.toArray(new IAnimatable[animatables.size()]);
for (IAnimatable animatable : anins) {
animatable.update();
}
}
});
timer.start();
}
public void addAnimatable(IAnimatable animatable) {
animatables.add(animatable);
}
public void removeAnimatable(IAnimatable animatable) {
animatables.remove(animatable);
}
}
public interface IAnimatable {
public void update();
}
public interface IMoveAnimatable extends IAnimatable{
public JComponent getSourceComponent();
public IImportable getImportable();
}
所以Animator
是核心「引擎」 ,它基本上是一個Swing Timer
,它只需在IAnimatable
上調用update
即可管理。這種方法的意圖是你可以運行一些動畫,但是它不會降低系統(很大程度上),因爲你只有一個更新/定時器點。
現在,通常我只是使用類似Timing Framework或Trident Framework甚至Universal Tween Engine
的IAnimatable
接口只是定義爲動漫提供功能的基本合同。
我們需要定義某種契約的可參與動畫製作過程和接收信息的定義對象,或者「目標」
public interface IImportable {
public JComponent getView();
public void importValue(String value);
}
public abstract class AbstractImportable extends JPanel implements IImportable {
@Override
public JComponent getView() {
return this;
}
}
現在它發生,我認爲我們可以挖掘到預先存在Transferable
API,它允許您也實現拖放(甚至複製/剪切和粘貼),這將用於定義一種查找機制,在該機制中,您將給定數據類型與潛在目標進行匹配,基於DataFlavor
...但我會離開你去研究它是如何工作的...... 核心機制基本上從當前容器中移除源組件,將它添加到AnimationPane
,將源組件移動到AnimationPane
上,然後將數據導入到目標中...
問題是,您需要將組件的位置從當前上下文轉換爲AnimationPane
。
組件的位置是相對於它的父母上下文。使用SwingUtilities.convertPoint(Component, Point, Component)
我們計算源組件和目標點的原點,相對於AnimationPane
。然後,我們在每次致電update
時計算動畫的進度。代替使用「增量」的運動,我們計算我們開始的時間和預定的持續時間(即1秒在這種情況下)之間的不同,這通常產生了更靈活的動畫
public class DefaultAnimatable implements IMoveAnimatable {
public static final double PLAY_TIME = 1000d;
private Long startTime;
private JComponent sourceComponent;
private IImportable importable;
private JComponent animationSurface;
private Point originPoint, destinationPoint;
private String value;
public DefaultAnimatable(JComponent animationSurface, JComponent sourceComponent, IImportable importable, String value) {
this.sourceComponent = sourceComponent;
this.importable = importable;
this.animationSurface = animationSurface;
this.value = value;
}
public String getValue() {
return value;
}
public JComponent getAnimationSurface() {
return animationSurface;
}
@Override
public JComponent getSourceComponent() {
return sourceComponent;
}
@Override
public IImportable getImportable() {
return importable;
}
@Override
public void update() {
if (startTime == null) {
System.out.println("Start");
IImportable importable = getImportable();
JComponent target = importable.getView();
originPoint = SwingUtilities.convertPoint(getSourceComponent().getParent(), getSourceComponent().getLocation(), getAnimationSurface());
destinationPoint = SwingUtilities.convertPoint(target.getParent(), target.getLocation(), getAnimationSurface());
destinationPoint.x = destinationPoint.x + ((target.getWidth() - getSourceComponent().getWidth())/2);
destinationPoint.y = destinationPoint.y + ((target.getHeight() - getSourceComponent().getHeight())/2);
Container parent = getSourceComponent().getParent();
getAnimationSurface().add(getSourceComponent());
getSourceComponent().setLocation(originPoint);
parent.invalidate();
parent.validate();
parent.repaint();
startTime = System.currentTimeMillis();
}
long duration = System.currentTimeMillis() - startTime;
double progress = Math.min(duration/PLAY_TIME, 1d);
Point location = new Point();
location.x = progress(originPoint.x, destinationPoint.x, progress);
location.y = progress(originPoint.y, destinationPoint.y, progress);
getSourceComponent().setLocation(location);
getAnimationSurface().repaint();
if (progress == 1d) {
getAnimationSurface().remove(getSourceComponent());
Animator.INSTANCE.removeAnimatable(this);
animationCompleted();
}
}
public int progress(int startValue, int endValue, double fraction) {
int value = 0;
int distance = endValue - startValue;
value = (int) Math.round((double) distance * fraction);
value += startValue;
return value;
}
protected void animationCompleted() {
getImportable().importValue(getValue());
}
}
好,現在這產生一個線性動畫,這是很無聊,現在如果你有足夠的時間,你可以創建一個地役權,如this或只是使用其中一個動畫框架...
現在,我們需要把它放在一起...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
public class AnimationTest {
public static void main(String[] args) {
new AnimationTest();
}
public AnimationTest() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
AnimationPane animationPane = new AnimationPane();
LeftPane leftPane = new LeftPane(animationPane);
RightPane rightPane = new RightPane();
leftPane.setImportabale(rightPane);
rightPane.setImportabale(leftPane);
JFrame frame = new JFrame("Testing");
frame.setLayout(new GridLayout(1, 2));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(leftPane, BorderLayout.WEST);
frame.add(rightPane, BorderLayout.WEST);
frame.setGlassPane(animationPane);
animationPane.setVisible(true);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class RightPane extends AbstractImportable {
private IImportable source;
private JButton imported;
private String importedValue;
public RightPane() {
setLayout(new GridBagLayout());
setBorder(new LineBorder(Color.DARK_GRAY));
}
public void setImportabale(IImportable source) {
this.source = source;
}
@Override
public void importValue(String value) {
if (imported != null) {
// May re-animate the movement back...
remove(imported);
}
importedValue = value;
imported = new JButton(">> " + value + "<<");
add(imported);
revalidate();
repaint();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public class LeftPane extends AbstractImportable {
private IImportable importable;
public LeftPane(AnimationPane animationPane) {
setLayout(new GridBagLayout());
JButton btn = new JButton("Lefty");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
DefaultAnimatable animatable = new DefaultAnimatable(animationPane, btn, importable, "Lefty");
Animator.INSTANCE.addAnimatable(animatable);
}
});
add(btn);
setBorder(new LineBorder(Color.DARK_GRAY));
}
public void setImportabale(IImportable target) {
this.importable = target;
}
@Override
public void importValue(String value) {
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
爲什麼要移動按鈕,爲什麼不移動信息N + – MadProgrammer
@MadProgrammer我也會移動信息,但首先我正在努力移動卡片,以便爲用戶提供關於發生什麼事情的直觀表示。我怎樣才能移動按鈕? – Sybren
你想要動畫嗎?或者你只是想讓第一個內容更新其他按鈕? – MadProgrammer