用按钮在屏幕上移动球。无法编程初始位置。

时间:2013-09-26 06:16:55

标签: java swing graphics2d

所以我正在做这个练习,我需要创建一个程序,通过按下四个按钮之一在屏幕上移动一个小球。我已经完成了,但后来我想把初始位置放在屏幕的中心,所以我将值getWidth()/ 2分配给xCoord,将getHeight()/ 2分配给yCoord(首先我没有构造函数,然后当它不起作用时,我添加了构造函数并添加了repaint(),因此将调用paintComponent())但是当我启动程序时,球仍然在左上角。我怎样才能解决这个问题? 附:我将非常感谢对代码的任何评论。谢谢。

package movingaball;
import java.awt.BorderLayout;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class MovingABall extends JFrame {
    private JButton jbtLeft = new JButton("Left");
    private JButton jbtRight = new JButton("Right");
    private JButton jbtUp = new JButton("Up");
    private JButton jbtDown = new JButton("Down");
    private BallPanel ballPanel = new BallPanel();

    public MovingABall () {
        JPanel buttonPanel = new JPanel();

        buttonPanel.add(jbtLeft);
        buttonPanel.add(jbtRight);
        buttonPanel.add(jbtUp);
        buttonPanel.add(jbtDown);


        this.add(ballPanel);
        this.add(buttonPanel, BorderLayout.SOUTH);
        jbtLeft.addActionListener(new ButtonListener());
        jbtRight.addActionListener(new ButtonListener());
        jbtUp.addActionListener(new ButtonListener());
        jbtDown.addActionListener(new ButtonListener());

    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        MovingABall mainWondow = new MovingABall();
        mainWondow.setTitle("Moving a ball");
        mainWondow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mainWondow.setSize(300, 200);
        mainWondow.setVisible(true);
    }

    class ButtonListener implements ActionListener {
        public void actionPerformed(ActionEvent buttonPressed) {
            if (buttonPressed.getSource() == jbtLeft)
                ballPanel.left();
            else if (buttonPressed.getSource() == jbtRight)
                ballPanel.right();
            else if (buttonPressed.getSource() == jbtUp)
                ballPanel.up();
            else if (buttonPressed.getSource() == jbtDown)
                ballPanel.down();
        }

    }

    class BallPanel extends JPanel {
        private int xCoord = 10;
        private int yCoord = 10;
        public BallPanel() {

            xCoord = getWidth()/2;
            yCoord = getHeight()/2;
            repaint();

        }


        @Override
        public void setBackground(Color bg) {
            super.setBackground(bg); //To change body of generated methods, choose Tools | Templates.
        }
        public void left() {
            xCoord-=5;
            repaint();         
        }

        public void right() {
            xCoord+=5;
            repaint();
        }
        public void up() {
            yCoord-=5;
            repaint();
        }

        public void down() {
            yCoord+=5;
            repaint();
        }



        protected void paintComponent(Graphics aBall) {
            super.paintComponent(aBall);
            System.out.println("X" + getWidth());
            aBall.drawOval(xCoord, yCoord, 10, 10);
        }

    }
}

3 个答案:

答案 0 :(得分:2)

你可以

ComponentListener事件使用ComponentResized和侦听器,但在组件达到最终屏幕尺寸之前,可以多次调用此事件。

你可以

使用AncestorListener收听ancestorAdded,但它遇到与ComponentListener

相同的问题

你可以

使用HierarchyListener倾听hierarchyChanged,但它遇到与ComponentListenerAncestorListener

相同的问题

你可以

覆盖doLayout,但这会遇到与ComponentListenerAncestorListenerHierarchyListener ...

相同的问题

那该怎么办?

我们需要的是知道组件在第一次显示时最后一次调整大小的时间。根据我的测试,我发现doLayout的{​​{1}}和hierarchyChanged是好的候选人。

现在问题出现了,因为我们只想在我们进入屏幕之前使用它们,之后我们就不在乎......

所以,我们需要做的第一件事就是将HierarchyListener初始化为某些“无效”值......

x/yCoord

这给了我们一些线索,我们仍然需要“设置”坐标......

接下来,我们需要一些方法来设置可中断的回叫。某种方式可以在我们选择的“监听器”和我们实际更新坐标的时间之间注入一个短暂的延迟,但如果“侦听器”被触发则可以重置....

private int xCoord = -1; private int yCoord = -1; 是一个很好的选择。我可以在后台等待一段指定的时间,如果我们需要它可以重新启动...

javax.swing.Timer

最后,当我们选择的“听众”被触发时,需要“重新启动”计时器。

为简单起见,我去了 private Timer resizeTimer; public BallPanel() { resizeTimer = new Timer(125, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // Only update the coorinates if they are invalid... if (xCoord < 0 && yCoord < 0) { xCoord = getWidth() / 2; yCoord = getHeight() / 2; repaint(); } } }); resizeTimer.setRepeats(false); ....

doLayout

现在,你可能需要玩延迟,我发现减速250毫秒,但那只是我;)

答案 1 :(得分:1)

请参阅代码中的注释。

Centered Ball

import java.awt.BorderLayout;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class MovingABall extends JFrame {

    private JButton jbtLeft = new JButton("Left");
    private JButton jbtRight = new JButton("Right");
    private JButton jbtUp = new JButton("Up");
    private JButton jbtDown = new JButton("Down");
    private BallPanel ballPanel = new BallPanel();

    public MovingABall () {
        JPanel buttonPanel = new JPanel();

        buttonPanel.add(jbtLeft);
        buttonPanel.add(jbtRight);
        buttonPanel.add(jbtUp);
        buttonPanel.add(jbtDown);

        ballPanel.setBackground(Color.RED);
        this.add(ballPanel);
        this.add(buttonPanel, BorderLayout.SOUTH);
        jbtLeft.addActionListener(new ButtonListener());
        jbtRight.addActionListener(new ButtonListener());
        jbtUp.addActionListener(new ButtonListener());
        jbtDown.addActionListener(new ButtonListener());
    }

    public static void main(String[] args) {
        // Should be called on the EDT!
        MovingABall mainWondow = new MovingABall();
        mainWondow.setTitle("Moving a ball");
        mainWondow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // Don't pack here.  Instead return a preferred size for the
        // custom comonent end..
        //mainWondow.setSize(300, 200);
        // ..pack() the window.
        mainWondow.pack();
        mainWondow.setVisible(true);
    }

    class ButtonListener implements ActionListener {
        public void actionPerformed(ActionEvent buttonPressed) {
            if (buttonPressed.getSource() == jbtLeft)
                ballPanel.left();
            else if (buttonPressed.getSource() == jbtRight)
                ballPanel.right();
            else if (buttonPressed.getSource() == jbtUp)
                ballPanel.up();
            else if (buttonPressed.getSource() == jbtDown)
                ballPanel.down();
        }
    }

    class BallPanel extends JPanel {
        private int xCoord = -1;
        private int yCoord = -1;
        private Dimension preferredSize = new Dimension(300,200);

/*  Harmful to our logic..
        public BallPanel() {
            xCoord = getWidth()/2;
            yCoord = getHeight()/2;
            repaint();
        }
*/
/*  A good compiler would remove this..
        @Override
        public void setBackground(Color bg) {
            super.setBackground(bg);
        } */

        public void left() {
            xCoord-=5;
            repaint();
        }

        public void right() {
            xCoord+=5;
            repaint();
        }
        public void up() {
            yCoord-=5;
            repaint();
        }

        public void down() {
            yCoord+=5;
            repaint();
        }

        /** Suggest a size to the layout manager. */
        @Override
        public Dimension getPreferredSize() {
            return preferredSize;
        }

        protected void paintComponent(Graphics aBall) {
            super.paintComponent(aBall);
            // This will center the ball if it is the first time painted
            // OR if the x or y co-ord goes off the left/top edge.
            // Further logic left to user..
            if (xCoord<0 || yCoord<0) {
                xCoord = getWidth()/2;
                yCoord = getHeight()/2;
            }
            System.out.println("X" + getWidth());
            aBall.drawOval(xCoord, yCoord, 10, 10);
        }
    }
}

答案 2 :(得分:0)

您已经走上了正确的轨道,但过早地调用了getWidth()和getHeight()方法。他们仍然会返回零,因为尚未设置大小:


在构造函数中调用GetWidth()和getHeight():

    MovingABall mainWondow = new MovingABall();
    mainWondow.setTitle("Moving a ball");
    mainWondow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

但是尺寸设置在这里:

    mainWondow.setSize(300, 200);
    mainWondow.setVisible(true);

那么什么时候采取行动呢?您可以覆盖setSize()课程中的setVisible()MovingABall或听取componentShown()

Listen for when a Component is Shown for the First Time建议使用HierarchyListener,这可能是最可靠的方法,但这里有点矫枉过正。

编辑: 最简单的事情可能仍然是更简单,我刚刚意识到:将BallPanel初始化为10,10但不是150,100:)

更严重的是: 将维度添加到MainWindow和BallPanel的构造函数中,而不是在MainWindow上调用setSize,您可以毫无困难地可靠地获得一致的初始状态。