Algo parece estar mal con el diseño, JButton muestra un comportamiento inesperado al cambiar el tamaño de la ventana

Resuelto nIcE cOw asked hace 12 años • 4 respuestas

JRE Versión 1.7 Actualización 3

COMPORTAMIENTO ESPERADO

Mientras ejecuto el programa, funciona como se esperaba, todo funciona sin problemas. Como cuando hago clic en STOP JButtonla animación se detiene y el texto en la misma JButtoncambia a START. Ahora, cuando hago clic en BALL COLOUR JButton, el color de los BALLcambios, así como el color de BALL COLOUR JBUTTON, también cambia al de BALL. Todo este comportamiento funciona si ejecuto mi aplicación tal como está sin cambiar el tamaño.

COMPORTAMIENTO INESPERADO

Pero cuando lo hago RESIZE, JFrameal tirar de él Right Side, es cuando se muestra un comportamiento inesperado de mi aplicación, en el sentido de que si presiono STOP JButtony luego hago clic en BALL COLOURel botón, el texto en el que JButtonse hizo clic anteriormente cuyo texto cambió STARTcambiará STOPnuevamente cuando no debería. será, así como el color de la BALL COLOUR JButtonpermanecerá sin cambios o cambiará a BLUE, cuando debería cambiarse al color de la bola. Adjunto las fotos para más información. Pero si intentas cambiar su tamaño a su tamaño original o más cerca de él, entonces todo volverá a la normalidad. Por qué está pasando esto ? Cualquier idea o pista será muy apreciada.

Mientras mi aplicación se ejecuta con el COMPORTAMIENTO ESPERADO como se describe anteriormente:

Comportamiento esperado

Y aquí el COMPORTAMIENTO INESPERADO

Comportamiento inesperado

LÍNEA DE FONDO :

¿Por qué la aplicación se ejecuta como de costumbre, como debería ser, en el momento BEGINNING, pero no cuando RESIZEDla arrastra RIGHT SIDE, pero nuevamente, si la lleva a su tamaño original o más cerca de él, las cosas vuelven a la normalidad y funciona como se esperaba?

Entonces, considerando el escenario, ¿estoy haciendo algo mal en el programa? ¿O es esta exactamente la situación en la que debería usar SwingWorker, o es un problema con Layout, o algo oculto relacionado con Content Pane? Por favor pon un poco de luz :-)

Aquí está el código que estoy usando, lo he reducido al mínimo, como creo que demuestra mi problema:

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

public class BallAnimation
{
    private int x;
    private int y;
    private boolean positiveX;
    private boolean positiveY;
    private boolean isTimerRunning; 
    private int speedValue;
    private int diameter; 
    private DrawingArea drawingArea;    
    private Timer timer;
    private int colourCounter;
     Color[] colours = {
                            Color.BLUE.darker(),
                            Color.MAGENTA.darker(),
                            Color.BLACK.darker(),
                            Color.RED.darker(),
                            Color.PINK.darker(),
                            Color.CYAN.darker(),
                            Color.DARK_GRAY.darker(),
                            Color.YELLOW.darker(),
                            Color.GREEN.darker()
                       };

    private Color backgroundColour;
    private Color foregroundColour; 

    private ActionListener timerAction = new ActionListener()
    {
        public void actionPerformed(ActionEvent ae)
        {
            x = getX();
            y = getY();
            drawingArea.setXYColourValues(x, y, backgroundColour
                                        , foregroundColour);
        }       
    };

    private JPanel buttonPanel;
    private JButton startStopButton;
    private JButton speedIncButton;
    private JButton speedDecButton;
    private JButton resetButton;
    private JButton colourButton;
    private JButton exitButton;

    private ComponentAdapter componentAdapter = new ComponentAdapter()
    {
        public void componentResized(ComponentEvent ce)
        {
            timer.restart();
            startStopButton.setText("STOP");
            isTimerRunning = true;
        }
    };  

    public BallAnimation()
    {
        x = y = 0;
        positiveX = positiveY = true;
        speedValue = 1;
        colourCounter = 0;
        isTimerRunning = false;
        diameter = 50;
        backgroundColour = Color.WHITE.brighter();
        foregroundColour = colours[colourCounter];
        timer = new Timer(10, timerAction);
    }

    private void createAndDisplayGUI()
    {
        JFrame frame = new JFrame("Ball Animation");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationByPlatform(true);

        drawingArea = new DrawingArea(x, y
                            , backgroundColour, foregroundColour, diameter);
        drawingArea.addComponentListener(componentAdapter);

        frame.add(makeButtonPanel(), BorderLayout.LINE_END);
        frame.add(drawingArea, BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);     
    }

    private JPanel makeButtonPanel()
    {
        buttonPanel = new JPanel();
        buttonPanel.setLayout(new GridLayout(0, 1));
        buttonPanel.setBorder(BorderFactory.createLineBorder(
                                    Color.DARK_GRAY, 5, true));

        startStopButton = new JButton("START");
        startStopButton.setBackground(Color.GREEN.darker());
        startStopButton.setForeground(Color.WHITE.brighter());
        startStopButton.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent ae)
            {
                System.out.println("START/STOP JButton Clicked!");
                if (!isTimerRunning)
                {
                    startStopButton.setText("STOP");
                    timer.start();
                    isTimerRunning = true;
                    buttonPanel.revalidate();
                    buttonPanel.repaint();
                }
                else if (isTimerRunning)
                {
                    startStopButton.setText("START");
                    timer.stop();
                    isTimerRunning = false;
                    buttonPanel.revalidate();
                    buttonPanel.repaint();
                }
            }
        });
        startStopButton.setBorder(BorderFactory.createLineBorder(
                                    Color.WHITE, 4, true));
        buttonPanel.add(startStopButton);

        colourButton = new JButton("BALL COLOUR");
        colourButton.setBackground(colours[colourCounter]);
        colourButton.setForeground(Color.WHITE);
        colourButton.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent ae)
            {
                System.out.println("COLOUR JButton Clicked!");
                //timer.restart();
                colourCounter++;
                if (colourCounter == 9)
                    colourCounter = 0;
                foregroundColour = colours[colourCounter];
                drawingArea.setXYColourValues(x, y, backgroundColour
                                                    , foregroundColour);
                //drawingArea.setForegroundForBall(foregroundColour);
                colourButton.setBackground(foregroundColour);
                colourButton.revalidate();
                colourButton.repaint();
                //timer.start();
            }
        });
        colourButton.setBorder(BorderFactory.createLineBorder(
                                    Color.WHITE, 2, true));
        buttonPanel.add(colourButton);

        exitButton = new JButton("EXIT");
        exitButton.setBackground(Color.RED.darker());
        exitButton.setForeground(Color.WHITE.brighter());
        exitButton.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent ae)
            {
                System.out.println("EXIT JButton Clicked!");
                timer.stop();
                System.exit(0);
            }
        });
        exitButton.setBorder(BorderFactory.createLineBorder(
                                    Color.RED.darker().darker(), 4, true));
        buttonPanel.add(exitButton);

        return buttonPanel;
    }

    private int getX()
    {
        if (x < 0)
            positiveX = true;
        else if (x >= drawingArea.getWidth() - diameter)
            positiveX = false;
        return (calculateX());
    }

    private int calculateX()
    {
        if (positiveX)
            return (x += speedValue);
        else
            return (x -= speedValue);
    }

    private int getY()
    {
        if (y < 0)
            positiveY = true;
        else if (y >= drawingArea.getHeight() - diameter)
            positiveY = false;
        return (calculateY());
    }

    private int calculateY()
    {
        if (positiveY)
            return (y += speedValue);
        else 
            return (y -= speedValue);
    }

    public static void main(String... args)
    {
        Runnable runnable = new Runnable()
        {
            public void run()
            {
                new BallAnimation().createAndDisplayGUI();
            }
        };
        SwingUtilities.invokeLater(runnable);
    }
}

class DrawingArea extends JComponent
{
    private int x;
    private int y;
    private int ballDiameter;
    private Color backgroundColor;
    private Color foregroundColor;

    public DrawingArea(int x, int y
                , Color bColor, Color fColor, int dia)
    {
        this.x = x;
        this.y = y;
        ballDiameter = dia;
        backgroundColor = bColor;
        foregroundColor = fColor;
        setBorder(BorderFactory.createLineBorder(
                        Color.DARK_GRAY.darker(), 5, true));
    }

    public void setXYColourValues(int x, int y
                            , Color bColor, Color fColor)
    {
        this.x = x;
        this.y = y;
        backgroundColor = bColor;
        foregroundColor = fColor;
        repaint();
    }

    public Dimension getPreferredSize()
    {
        return (new Dimension(500, 400));
    }

    public void paintComponent(Graphics g)
    {
        g.setColor(backgroundColor);
        g.fillRect(0, 0, getWidth(), getHeight());
        g.setColor(foregroundColor);
        g.fillOval(x, y, ballDiameter, ballDiameter);
    }
}

**ÚLTIMA EDICIÓN: **

EL ÚLTIMO

nIcE cOw avatar Mar 24 '12 14:03 nIcE cOw
Aceptado

El problema con su muy buen ejemplo puede depender de la plataforma, pero puedo ofrecer algunas observaciones:

  • No estás agregando ni quitando componentes, por lo que no necesitas revalidate().

  • Debido a que el color de fondo es una propiedad vinculada de los botones, no necesita las llamadas posteriores a repaint().

  • Lo necesitarepaint() en su costumbre , pero es posible que DrawingAreadesee experimentar agregando soporte para cambios de propiedad, como se sugiere aquí .

  • Color.whiteno puede ser brighter()y Color.blackno puede ser darker(); Color.darkGray.darker()es Color.black().

  • La siguiente variación utiliza a Queue<Color>para simplificar el cambio de colores.

la captura de pantalla

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

/** @see https://stackoverflow.com/q/9849950/230513 */
public class BallAnimation {

    private int x;
    private int y;
    private boolean positiveX;
    private boolean positiveY;
    private boolean isTimerRunning;
    private int speedValue;
    private int diameter;
    private DrawingArea drawingArea;
    private Timer timer;
    private Queue<Color> clut = new LinkedList<Color>(Arrays.asList(
        Color.BLUE.darker(),
        Color.MAGENTA.darker(),
        Color.BLACK,
        Color.RED.darker(),
        Color.PINK,
        Color.CYAN.darker(),
        Color.DARK_GRAY,
        Color.YELLOW.darker(),
        Color.GREEN.darker()));
    private Color backgroundColour;
    private Color foregroundColour;
    private ActionListener timerAction = new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent ae) {
            x = getX();
            y = getY();
            drawingArea.setXYColourValues(x, y, backgroundColour, foregroundColour);
        }
    };
    private JPanel buttonPanel;
    private JButton startStopButton;
    private JButton speedIncButton;
    private JButton speedDecButton;
    private JButton resetButton;
    private JButton colourButton;
    private JButton exitButton;
    private ComponentAdapter componentAdapter = new ComponentAdapter() {

        @Override
        public void componentResized(ComponentEvent ce) {
            timer.restart();
            startStopButton.setText("Stop");
            isTimerRunning = true;
        }
    };

    public BallAnimation() {
        x = y = 0;
        positiveX = positiveY = true;
        speedValue = 1;
        isTimerRunning = false;
        diameter = 50;
        backgroundColour = Color.white;
        foregroundColour = clut.peek();
        timer = new Timer(10, timerAction);
    }

    private void createAndDisplayGUI() {
        JFrame frame = new JFrame("Ball Animation");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationByPlatform(true);

        drawingArea = new DrawingArea(x, y, backgroundColour, foregroundColour, diameter);
        drawingArea.addComponentListener(componentAdapter);

        frame.add(makeButtonPanel(), BorderLayout.LINE_END);
        frame.add(drawingArea, BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);
    }

    private JPanel makeButtonPanel() {
        buttonPanel = new JPanel(new GridLayout(0, 1));
        buttonPanel.setBorder(BorderFactory.createLineBorder(Color.darkGray, 5));

        startStopButton = new JButton("Start");
        startStopButton.setOpaque(true);
        startStopButton.setForeground(Color.white);
        startStopButton.setBackground(Color.green.darker());
        startStopButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ae) {
                if (!isTimerRunning) {
                    startStopButton.setText("Stop");
                    timer.start();
                    isTimerRunning = true;
                } else if (isTimerRunning) {
                    startStopButton.setText("Start");
                    timer.stop();
                    isTimerRunning = false;
                }
            }
        });
        startStopButton.setBorder(BorderFactory.createLineBorder(Color.gray, 4));
        buttonPanel.add(startStopButton);

        colourButton = new JButton("Change Color");
        colourButton.setOpaque(true);
        colourButton.setForeground(Color.white);
        colourButton.setBackground(clut.peek());
        colourButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ae) {
                //timer.restart();
                clut.add(clut.remove());
                foregroundColour = clut.peek();
                drawingArea.setXYColourValues(x, y, backgroundColour, foregroundColour);
                colourButton.setBackground(foregroundColour);
            }
        });
        colourButton.setBorder(BorderFactory.createLineBorder(Color.gray, 4));
        buttonPanel.add(colourButton);

        exitButton = new JButton("Exit");
        exitButton.setBackground(Color.red);
        exitButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ae) {
                timer.stop();
                System.exit(0);
            }
        });
        exitButton.setBorder(BorderFactory.createLineBorder(Color.red.darker(), 4));
        buttonPanel.add(exitButton);

        return buttonPanel;
    }

    private int getX() {
        if (x < 0) {
            positiveX = true;
        } else if (x >= drawingArea.getWidth() - diameter) {
            positiveX = false;
        }
        return (calculateX());
    }

    private int calculateX() {
        if (positiveX) {
            return (x += speedValue);
        } else {
            return (x -= speedValue);
        }
    }

    private int getY() {
        if (y < 0) {
            positiveY = true;
        } else if (y >= drawingArea.getHeight() - diameter) {
            positiveY = false;
        }
        return (calculateY());
    }

    private int calculateY() {
        if (positiveY) {
            return (y += speedValue);
        } else {
            return (y -= speedValue);
        }
    }

    public static void main(String... args) {
        Runnable runnable = new Runnable() {

            @Override
            public void run() {
                new BallAnimation().createAndDisplayGUI();
            }
        };
        SwingUtilities.invokeLater(runnable);
    }
}

class DrawingArea extends JComponent {

    private int x;
    private int y;
    private int ballDiameter;
    private Color backgroundColor;
    private Color foregroundColor;

    public DrawingArea(int x, int y, Color bColor, Color fColor, int dia) {
        this.x = x;
        this.y = y;
        ballDiameter = dia;
        backgroundColor = bColor;
        foregroundColor = fColor;
        setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 5));
    }

    public void setXYColourValues(int x, int y, Color bColor, Color fColor) {
        this.x = x;
        this.y = y;
        backgroundColor = bColor;
        foregroundColor = fColor;
        repaint();
    }

    @Override
    public Dimension getPreferredSize() {
        return (new Dimension(500, 400));
    }

    @Override
    public void paintComponent(Graphics g) {
        g.setColor(backgroundColor);
        g.fillRect(0, 0, getWidth(), getHeight());
        g.setColor(foregroundColor);
        g.fillOval(x, y, ballDiameter, ballDiameter);
    }
}
trashgod avatar Mar 24 '2012 15:03 trashgod

Parece que algo anda mal con BorderLayout.LINE_ENDla cosa, solo que cuando lo coloco buttonPanel, LINE_ENDobtengo resultados no deseados. Intenté usar solo uno JButton, en lugar de tres como última medida, para resolver las cosas. Ahora el problema que suele aparecer como se muestra en esta imagen:

LINE_END

se ha solucionado cambiando la posición del JButtonPanel LINE_STARTo usando JRE version 1.6 update 31, en la imagen de la siguiente manera:

LINE_START

Aquí está el código utilizado para este ejemplo:

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

public class BallAnimation
{
    private int x;
    private int y;
    private boolean positiveX;
    private boolean positiveY;
    private boolean isTimerRunning; 
    private int speedValue;
    private int diameter; 
    private DrawingArea drawingArea;    
    private Timer timer;
    private int colourCounter;
     Color[] colours = {
                            Color.BLUE.darker(),
                            Color.MAGENTA.darker(),
                            Color.BLACK.darker(),
                            Color.RED.darker(),
                            Color.PINK.darker(),
                            Color.CYAN.darker(),
                            Color.DARK_GRAY.darker(),
                            Color.YELLOW.darker(),
                            Color.GREEN.darker()
                       };

    private Color backgroundColour;
    private Color foregroundColour; 

    private ActionListener timerAction = new ActionListener()
    {
        public void actionPerformed(ActionEvent ae)
        {
            x = getX();
            y = getY();
            drawingArea.setXYColourValues(x, y, backgroundColour
                                        , foregroundColour);
        }       
    };

    private JPanel buttonPanel;
    private JButton startStopButton;
    private JButton speedIncButton;
    private JButton speedDecButton;
    private JButton resetButton;
    private JButton colourButton;
    private JButton exitButton;

    private ComponentAdapter componentAdapter = new ComponentAdapter()
    {
        public void componentResized(ComponentEvent ce)
        {
            timer.restart();
        }
    };  

    public BallAnimation()
    {
        x = y = 0;
        positiveX = positiveY = true;
        speedValue = 1;
        colourCounter = 0;
        isTimerRunning = false;
        diameter = 50;
        backgroundColour = Color.WHITE.brighter();
        foregroundColour = colours[colourCounter];
        timer = new Timer(10, timerAction);
    }

    private void createAndDisplayGUI()
    {
        JFrame frame = new JFrame("Ball Animation");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationByPlatform(true);

        drawingArea = new DrawingArea(x, y
                            , backgroundColour, foregroundColour, diameter);
        drawingArea.addComponentListener(componentAdapter);

        frame.add(makeButtonPanel(), BorderLayout.LINE_START);
        frame.add(drawingArea, BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);     
    }

    private JPanel makeButtonPanel()
    {
        buttonPanel = new JPanel();
        buttonPanel.setLayout(new GridLayout(0, 1));
        buttonPanel.setBorder(BorderFactory.createLineBorder(
                                    Color.DARK_GRAY, 5, true));

        colourButton = new JButton("BALL COLOUR");
        colourButton.setOpaque(true);
        colourButton.setBackground(colours[colourCounter]);
        colourButton.setForeground(Color.WHITE);
        colourButton.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent ae)
            {
                System.out.println("COLOUR JButton Clicked!");
                if (timer.isRunning())
                    timer.stop();
                colourCounter++;
                if (colourCounter == 9)
                    colourCounter = 0;
                foregroundColour = colours[colourCounter];
                drawingArea.setXYColourValues(x, y, backgroundColour
                                                    , foregroundColour);
                colourButton.setBackground(foregroundColour);
                if (!timer.isRunning())
                    timer.start();
            }
        });
        colourButton.setBorder(BorderFactory.createLineBorder(
                                    Color.WHITE, 2, true));
        buttonPanel.add(colourButton);

        return buttonPanel;
    }

    private int getX()
    {
        if (x < 0)
            positiveX = true;
        else if (x >= drawingArea.getWidth() - diameter)
            positiveX = false;
        return (calculateX());
    }

    private int calculateX()
    {
        if (positiveX)
            return (x += speedValue);
        else
            return (x -= speedValue);
    }

    private int getY()
    {
        if (y < 0)
            positiveY = true;
        else if (y >= drawingArea.getHeight() - diameter)
            positiveY = false;
        return (calculateY());
    }

    private int calculateY()
    {
        if (positiveY)
            return (y += speedValue);
        else 
            return (y -= speedValue);
    }

    public static void main(String... args)
    {
        Runnable runnable = new Runnable()
        {
            public void run()
            {
                new BallAnimation().createAndDisplayGUI();
            }
        };
        SwingUtilities.invokeLater(runnable);
    }
}

class DrawingArea extends JComponent
{
    private int x;
    private int y;
    private int ballDiameter;
    private Color backgroundColor;
    private Color foregroundColor;

    public DrawingArea(int x, int y
                , Color bColor, Color fColor, int dia)
    {
        this.x = x;
        this.y = y;
        ballDiameter = dia;
        backgroundColor = bColor;
        foregroundColor = fColor;
        setBorder(BorderFactory.createLineBorder(
                        Color.DARK_GRAY.darker(), 5, true));
    }

    public void setXYColourValues(int x, int y
                            , Color bColor, Color fColor)
    {
        this.x = x;
        this.y = y;
        backgroundColor = bColor;
        foregroundColor = fColor;
        repaint();
    }

    public Dimension getPreferredSize()
    {
        return (new Dimension(500, 400));
    }

    public void paintComponent(Graphics g)
    {
        g.setColor(backgroundColor);
        g.fillRect(0, 0, getWidth(), getHeight());
        g.setColor(foregroundColor);
        g.fillOval(x, y, ballDiameter, ballDiameter);
    }
}
nIcE cOw avatar Mar 25 '2012 08:03 nIcE cOw

tal vez te ayude con dos partes de , creo que Graphics/2D está diseñado para usar Swing Timer exclusivamente,

mKorbel avatar Mar 24 '2012 07:03 mKorbel

No estoy seguro de haber encontrado una solución para su sistema, pero ajustar el código a

colourButton = new JButton( "BALL COLOUR" );
colourButton.setOpaque( true );
colourButton.setBackground( colours[ colourCounter ] );
colourButton.setForeground( Color.WHITE );

funciona en mi sistema (OS X con Java 1.7). Tenga en cuenta la setOpaquellamada, que es necesaria para que la setBackgroundllamada tenga algún efecto como se indica en el javadoc de ese método:

Establece el color de fondo de este componente. El color de fondo se utiliza sólo si el componente es opaco.

En OS X, sin esa setOpaquellamada, su código ni siquiera funciona antes de cambiar el tamaño

Robin avatar Mar 24 '2012 13:03 Robin