[英]Set JButton to the location of another JButton
我正在嘗試將JButton移動到另一個按鈕的位置,但是我要移動的按鈕移動到了錯誤的位置。 我的想法是,發生這種情況是因為我使用了多個JPanel。 我試過了: getLocationOnScreen
, getBounds
和getLocation
,但是它們都不起作用,如何解決呢? 當用戶通過單擊此卡在桌上或從玩家中選擇一張卡時,將設置目標,通過在頂部面板中單擊一張卡來設置發件人。 playerCardSpotTarget
和playerCardSpotSender
均為Card類型。 例如,當我嘗試移動8顆鑽石時,這張卡會移動到8和9桿后面的位置。
碼:
該事件屬於桌子上的藍卡和玩家的卡(我必須更改事件的名稱,我知道)。
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;
}
動畫化按鈕移動實際上並不是最困難的問題,最困難的問題是試圖將數據移動到可以管理的位置以及如何將源組件與目標連接起來。
首先,您需要一種可以跨容器邊界移動組件的方法。 雖然可能有幾種方法可以做到這一點,但最簡單的方法可能是使用框架的玻璃窗格
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());
}
}
好的,現在這會產生一個線性動畫,這很無聊,現在如果您有足夠的時間,您可以像這樣創建一個地役權,或者只使用其中一個動畫框架...
現在,我們需要將其放在一起...
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);
}
}
}
也許使用mousePressed()
,在移動卡時,將其按到目標。然后在該過程中,通過事件獲取有關JButton.getLocation()
的信息,然后需要解決兩張卡之間的沖突問題很好。當然,這是我的建議,您應該有更好的主意!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.