我试图将JButton移动到另一个的位置,但我要移动的按钮移动到错误的位置。我的想法是,这是因为我使用了多个JPanel。我尝试过:getLocationOnScreen
,getBounds
和getLocation
,但没有一个有效,如何解决?当用户通过单击此卡选择桌面上的卡或玩家选择目标时,通过单击顶部面板中的卡来设置发件人。 playerCardSpotTarget
和playerCardSpotSender
都是卡片类型。当我试图移动例如八颗钻石时,这张卡移动到八个和九个球杆后面的一个点。
代码:
此事件属于桌面上的蓝卡和玩家的牌(我必须更改事件的名称,我知道)。
private void PlayerOneMouseClicked(java.awt.event.MouseEvent evt){
playerCardSpotTarget=(Card)evt.getSource();
if(playerCardSpotTarget.isBorderPainted()){
playerCardSpotTarget.setBorderPainted(false);
}
else{
playerCardSpotTarget.setBorderPainted(true);
}
}
此事件属于顶部面板中的卡片。
private void MouseClicked(java.awt.event.MouseEvent evt) {
playerCardSpotSender=(Card)evt.getSource();
System.out.println(playerCardSpotSender.suit+" "+playerCardSpotSender.kind);
if (playerCardSpotTarget != null && playerCardSpotTarget.isBorderPainted()) {
playerCardSpotSender.setLocation(playerCardSpotTarget.getLocation());
System.out.println(playerCardSpotTarget.getLocationOnScreen());
System.out.println(playerCardSpotSender.getLocationOnScreen());
}
}
JFrame(BorderLayout.CENTER)中心面板的布局
JPanel centerPanelNorth;
JPanel centerPanelCenter;
JPanel centerPanelEast;
JPanel centerPanelSouth;
JPanel centerPanelWest;
JLabel tablePicture;
JPanel centerPanel;
centerPanel=new JPanel(new BorderLayout());
tablePicture = new JLabel(new ImageIcon(this.getClass().getResource(Constants.POKERTABLE_ICON)));
centerPanelNorth=new JPanel();
centerPanelEast=new JPanel();
centerPanelSouth=new JPanel();
centerPanelWest=new JPanel();
centerPanelCenter=new JPanel();
centerPanel.add(centerPanelCenter,BorderLayout.CENTER);
centerPanelCenter.add(tablePicture);
//add
tablePicture.add(boardCard1);
tablePicture.add(boardCard2);
tablePicture.add(boardCard3);
tablePicture.setLayout(new GridBagLayout());
//PLAYER NORTH
centerPanel.add(centerPanelNorth,BorderLayout.NORTH);
centerPanelNorth.add(playerOneCardOne);
centerPanelNorth.add(playerOneCardTwo);
//PLAYER EAST
centerPanel.add(centerPanelEast,BorderLayout.EAST);
centerPanelEast.setLayout(new BoxLayout(centerPanelEast,BoxLayout.X_AXIS));
centerPanelEast.add(playerTwoCardOne);
centerPanelEast.add(playerTwoCardTwo);
//PLAYER SOUTH
centerPanel.add(centerPanelSouth,BorderLayout.SOUTH);
centerPanelSouth.add(playerThreeCardOne);
centerPanelSouth.add(playerThreeCardTwo);
//PLAYER WEST
centerPanel.add(centerPanelWest,BorderLayout.WEST);
centerPanelWest.setLayout(new BoxLayout(centerPanelWest,BoxLayout.X_AXIS));
centerPanelWest.add(playerFourCardOne);
centerPanelWest.add(playerFourCardTwo);
Card.java
public class Card extends JButton{
int suit;
int kind;
boolean known;
String iconPath;
Integer boardPosition;
}
答案 0 :(得分:1)
动画按钮移动实际上并不是最困难的问题,最困难的问题是尝试移动数据,以便管理它以及如何将源组件与目标连接......
首先,您需要一种可以跨容器边界移动组件的方法。虽然可能有几种方法可以做到这一点,但最简单的方法是使用框架的玻璃窗格
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
是核心&#34;引擎&#34;,它基本上是一个Swing Timer
,只需在任何update
上调用IAnimatable
它可能正在管理。这种方法的目的是你可以运行许多动画,但它不会因为只有一个更新/计时器点而大大降低系统性能。
现在,通常我只使用Timing Framework或Trident Framework甚至Universal Tween Engine
之类的内容 IAnimatable
接口只定义了为动画提供功能的基本合约。
我们需要定义某种合约,定义可以参与动画过程和接收信息的对象,或者#34; target&#34;
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
,然后将数据导入目标... < / p>
问题是,您需要将组件的位置从其当前上下文转换为AnimationPane
。
组件位置与其父母上下文有关。使用SwingUtilities.convertPoint(Component, Point, Component)
我们计算相对于AnimationPane
的源组件和目标点的原点。然后,在每次调用update
时,我们计算动画的进度。而不是使用&#34; delta&#34;运动,我们计算我们开始的时间和预定义的持续时间(在这种情况下为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);
}
}
}
答案 1 :(得分:0)
也许使用mousePressed()
,当您移动卡片时,按下它直到目标。在此过程中,您将获得有关事件的JButton.getLocation()
的信息,而不是您需要解决的问题两张牌之间的碰撞问题。所以这很好!当然,这是我的建议,你应该有更好的想法!