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
答案 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的回答。