动画按钮动作实际上不是最难的问题,最难的问题是试图移动数据约在客场中,你可以管理它,以及如何将源组件与目标连接...
首先,您需要一种方法,通过该方法可以跨组件边界移动组件。虽然有可能是几个方法可以做到这一点,最简单的就是可能使用帧
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