Black outline while resizing JFrame

时间:2017-06-20 12:33:38

标签: java swing user-interface jframe transitions

I'm trying to create a smooth animation from one JPanel to the next where the second JPanel is both taller and wider than the first requiring me to rescale the JFrame. To do this I created the following code:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.WindowConstants;

public class Example1 extends JFrame
{

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public Example1()
    {
        initComponents();
    }

    public static void main(String[] args) 
    {
        try 
        {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); // get look and feel based on OS
        } 
        catch (ClassNotFoundException ex) // catch all errors that may occur
        {
            Logger.getLogger(Example1.class.getName()).log(Level.SEVERE, null, ex);
        } 
        catch (InstantiationException ex) 
        {
            Logger.getLogger(Example1.class.getName()).log(Level.SEVERE, null, ex);
        } 
        catch (IllegalAccessException ex) 
        {
            Logger.getLogger(Example1.class.getName()).log(Level.SEVERE, null, ex);
        } 
        catch (UnsupportedLookAndFeelException ex) 
        {
            Logger.getLogger(Example1.class.getName()).log(Level.SEVERE, null, ex);
        }

        EventQueue.invokeLater(new Runnable()
        {
            public void run() // run the class's constructor, therefore starting
                                // the UI being built
            {
                new Example1().setVisible(true);;
            }
        });
    }

    private WindowListener exitListener = new WindowAdapter() 
    {
        @Override
        public void windowClosing(WindowEvent e) 
        {
            closingEvent(); // if window closing, go to exit menu
        }
    };

    private void initComponents() // method to build initial view for user for installation
    {
        // instantiating elements of the GUI
        pnlStart = new JPanel();
        lblMain = new JLabel();
        lblDivider = new JLabel();
        lblTextPrompt = new JLabel();
        txtAccNum = new JTextField();
        btnNext = new JButton();
        btnExit = new JButton();

        pnlStart.setVisible(true);
        add(pnlStart); // adding the panel to the frame

        removeWindowListener(exitListener);
        addWindowListener(exitListener); // removing before adding the windowlistener, ensures there is only one listener there
        setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); // setting "x" button to do nothing except what exitListener does
        setPreferredSize(new Dimension(600, 400)); // setting measurements of jframe

        setTitle("Example 1.0"); // setting title on JFrame
        setResizable(false); // disabling resizing
        setLayout(null); // ensuring I can specify element positions
        setBackground(Color.WHITE); // setting background color

        lblMain.setText("<html>Please input a number below how many accounts you would like to<br>create: </html>"); // main label that explains what happens, html used for formatting
        lblMain.setFont(lblMain.getFont().deriveFont(18.0f)); // changing font size to 16
        lblMain.setBounds(27, 60, 540, 100); // setting position and measurements
        add(lblMain); // adding label to JFrame

        lblTextPrompt.setText("Amount of accounts (1-10):");
        lblTextPrompt.setFont(lblMain.getFont().deriveFont(16.0f));
        lblTextPrompt.setBounds(166, 190, 198, 18);
        lblTextPrompt.setLabelFor(txtAccNum);
        add(lblTextPrompt);

        txtAccNum.setFont(lblMain.getFont());
        txtAccNum.setBounds(374, 187, 50, 26);
        txtAccNum.addKeyListener(new KeyAdapter() 
        {
            public void keyTyped(KeyEvent e) 
            {
                if (txtAccNum.getText().length() >= 4) // limit textfield to 3 characters
                    e.consume();
            }
        });
        txtAccNum.addActionListener(new ActionListener() 
        {
            public void actionPerformed(ActionEvent e) 
            {
                AccDetails(Integer.parseInt(txtAccNum.getText()));
            }
        });
        add(txtAccNum);

        lblDivider.setText(""); // ensuring no text in label
        lblDivider.setBounds(10, 285, 573, 10); // setting bounds and position of dividing line
        lblDivider.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.LIGHT_GRAY)); // setting border to label for the dividing
        add(lblDivider); // adding it to JFrame

        btnNext.setText("Next"); // adding text to button for starting
        btnNext.setFont(lblMain.getFont().deriveFont(14.0f)); // setting font size
        btnNext.setBounds(495, 315, 80, 35); // positioning start button
        btnNext.addActionListener(new ActionListener() // add listener for action to run method
        {
            public void actionPerformed(ActionEvent evt) 
            {
                AccDetails(Integer.parseInt(txtAccNum.getText()));
            }
        });
        add(btnNext); // adding button to JFrame

        btnExit.setText("Exit"); // adding text to button for exiting
        btnExit.setFont(btnNext.getFont()); // getting font from start button
        btnExit.setBounds(20, 315, 80, 35); // positioning on form
        btnExit.addActionListener(new ActionListener() // add listener for action to run method
        {
            public void actionPerformed(ActionEvent evt) 
            {
                closingEvent(); // running cancel method (same method as hitting the "x" button on the form)
            }
        });

        add(btnExit); // adding button to JFrame
        repaint(); // repainting what is displayed if going coming from a different form
        revalidate(); // revalidate the elements that will be displayed
        pack(); // packaging everything up to use
        setLocationRelativeTo(null); // setting form position central
        txtAccNum.requestFocusInWindow(); // setting focus on start button when everything is loaded
    }

    private void AccDetails(int accNum) 
    {
        getContentPane().removeAll();
        // instantiating elements of the GUI
        pnlAccDetails = new JPanel();

        pnlAccDetails.setVisible(true);
        add(pnlAccDetails); // adding the panel to the frame

        removeWindowListener(exitListener);
        addWindowListener(exitListener); // removing before adding the windowlistener, ensures there is only one listener there
        setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); // setting "x" button to do nothing except what exitListener does

        while (sizeW != 750 && sizeH != 500)
        {
            setBackground(Color.BLACK);
            Point loc = getLocationOnScreen();
            setPreferredSize(new Dimension(sizeW, sizeH));
            pnlAccDetails.setPreferredSize(new Dimension(sizeW, sizeH));
            repaint();
            revalidate();
            pack();

            sizeW += 1.5;
            sizeH += 1;
            if (toggle)
            {
                setLocation((int)(loc.getX() - 0.75), (int)(loc.getY() - 0.5));
                toggle = false;
            }
            else
            {
                toggle = true;
            }

            try 
            {
                Thread.sleep(1);
            } 
            catch (InterruptedException e) 
            {
                e.printStackTrace();
            }
        }

        setTitle("Example 1.0"); // setting title on JFrame
        setResizable(false); // disabling resizing
        setLayout(null); // ensuring I can specify element positions
        setBackground(Color.WHITE); // setting background color

        repaint(); // repainting what is displayed if going coming from a different form
        revalidate(); // revalidate the elements that will be displayed
        pack(); // packaging everything up to use
        setLocationRelativeTo(null); // setting form position central
    }

    private void closingEvent() 
    {
        if (JOptionPane.showConfirmDialog(null, "<html><center>Are you sure you want to quit?</center></html>", "Quit?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) 
            System.exit(0); // output warning that it would cancel installation, if accepted...
        else // if not accepted...
        {
        }
    }

    // objects used in UI
    private JPanel pnlStart;
    private JPanel pnlAccDetails;
    private JLabel lblMain;
    private JLabel lblDivider;
    private JLabel lblTextPrompt;
    private JTextField txtAccNum;
    private JButton btnNext;
    private JButton btnExit;

    private int sizeW = 600;
    private int sizeH = 400;
    private boolean toggle = false;
}

While this code does work, during the resizing, the form doesn't retain it's background colour and instead has a black outline with the new measurements. I understand, from the research I've done, this is due to the rendering engine used. Is there anyway to force the render engine to run at each iteration or is there another way of doing this? I've seen the suggestion of using Universal Tween Engine however I couldn't find any resizing examples, especially for the JFrame. Thanks in advance

1 个答案:

答案 0 :(得分:1)

正如上面的评论(由@Sergiy Medvynskyy所述)阻止Swing Thread导致它无法正确呈现。使用Swing Timer,动画可以顺利运行。我用于解决方案的代码是:

    timer = new Timer (10, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent arg0) 
        {
            Point loc = getLocationOnScreen();
            setPreferredSize(new Dimension(sizeW, sizeH));
            pnlAccDetails.setPreferredSize(new Dimension(sizeW, sizeH));
            repaint();
            revalidate();
            pack();

            sizeW += 3;
            sizeH += 2;
            if (toggle)
            {
                setLocation((int)(loc.getX() - 0.75), (int)(loc.getY() - 0.5));
                toggle = false;
            }
            else
            {
                toggle = true;
            }

            if (sizeW == 750 && sizeH == 500)
            {
                timer.stop();
            }
        }
    });

    timer.start();

上面的代码用于代替原始问题中的while循环。感谢Sergiy Medvynskyy的回答。