将JButton设置为另一个JButton的位置

时间:2015-04-15 08:25:09

标签: java swing

我试图将JButton移动到另一个的位置,但我要移动的按钮移动到错误的位置。我的想法是,这是因为我使用了多个JPanel。我尝试过:getLocationOnScreengetBoundsgetLocation,但没有一个有效,如何解决?当用户通过单击此卡选择桌面上的卡或玩家选择目标时,通过单击顶部面板中的卡来设置发件人。 playerCardSpotTargetplayerCardSpotSender都是卡片类型。当我试图移动例如八颗钻石时,这张卡移动到八个和九个球杆后面的一个点。

enter image description here

代码:

此事件属于桌面上的蓝卡和玩家的牌(我必须更改事件的名称,我知道)。

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;
}

2 个答案:

答案 0 :(得分:1)

动画按钮移动实际上并不是最困难的问题,最困难的问题是尝试移动数据,以便管理它以及如何将源组件与目标连接......

Move me

首先,您需要一种可以跨容器边界移动组件的方法。虽然可能有几种方法可以做到这一点,但最简单的方法是使用框架的玻璃窗格

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 FrameworkTrident 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()的信息,而不是您需要解决的问题两张牌之间的碰撞问题。所以这很好!当然,这是我的建议,你应该有更好的想法!