1

この演習では、4 つのボタンのいずれかを押すことで画面上の小さなボールを動かすプログラムを作成する必要があります。完成しましたが、初期位置を画面中央にしたかったので、xCoord に getWidth()/2 、yCoord に getHeight()/2 という値を割り当てました (最初はコンストラクター、それが機能しなかったときに、コンストラクターを追加して repaint() を追加したため、paintComponent() が呼び出されます) が、プログラムを開始すると、ボールはまだ左上隅にあります。どうすればこれを修正できますか? PS コード全般に関するコメントもお待ちしています。ありがとうございました。

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

    }
}
4

3 に答える 3

2

あなたは出来る

ComponentListenerイベントにはリスナーを使用しComponentResizedますが、これはコンポーネントが最終的な画面サイズに達する前に複数回呼び出される可能性があります....

あなたは出来る

AncestorListenerのリスニングを使用しますancestorAddedが、 と同じ問題に悩まされます。ComponentListener

あなたは出来る

HierarchyListenerのリスニングを使用しますhierarchyChangedが、 and と同じ問題がComponentListenerあります。AncestorListener

あなたは出来る

オーバーライドしますが、これはand と同じdoLayout問題を抱えています ...ComponentListenerAncestorListenerHierarchyListener

じゃあ何をすればいいの?

必要なのは、コンポーネントが最初に表示されたときに最後にサイズ変更されたときを知ることです。私のテストから、私は良い候補者doLayouthierarchyChangedあることがわかりました。HierarchyListener

問題が発生するのは、画面に表示されるまでのみ使用したいためです。その後は気にしません...

したがって、最初にx/yCoords を「無効な」値に初期化する必要があります...

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....に行きました。

@Override
public void doLayout() {
    super.doLayout();
    if (xCoord < 0 && yCoord < 0) {
        resizeTimer.restart();
    }
}

さて、遅延をいじる必要があるかもしれません.250ミリ秒遅くなることがわかりましたが、それは私だけです;)

于 2013-09-26T06:54:35.377 に答える
1

コード内のコメントを参照してください。

センターボール

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);
        }
    }
}
于 2013-09-26T06:55:18.947 に答える
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()

コンポーネントが初めて表示されたときにリッスンすると、HierarchyListener が提案されます。これは最も信頼できるアプローチかもしれませんが、ここでは少しやり過ぎです。

編集:おそらく動作する可能性のある最も簡単なことは、さらに簡単なことです.BallPanelを10,10ではなく150,100に初期化してください:)

もう少し深刻なこと: MainWindow で setSize を呼び出す代わりに、MainWindow と BallPanel の両方のコンストラクターにディメンションを追加すると、手間をかけずに一貫した初期状態を確実に取得できます。

于 2013-09-26T06:31:25.703 に答える