I am trying to hide a JSplitPane with animation. By hide, I mean to setDividerLocation(0) so its left component is invisible (technically it is visible, but with zero width):
public class SplitPaneTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
JPanel leftPanel = new JPanel(new BorderLayout());
leftPanel.setBorder(BorderFactory.createLineBorder(Color.green));
JPanel rightPanel = new JPanel(new GridLayout(60, 60));
for (int i = 0; i < 60 * 60; i++) {
// rightPanel.add(new JLabel("s"));
}
rightPanel.setBorder(BorderFactory.createLineBorder(Color.red));
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightPanel);
frame.add(splitPane);
JButton button = new JButton("Press me to hide");
button.addActionListener(e -> hideWithAnimation(splitPane));
leftPanel.add(button, BorderLayout.PAGE_START);
frame.setMaximumSize(new Dimension(800, 800));
frame.setSize(800, 800);
frame.setLocationByPlatform(true);
frame.setVisible(true);
});
}
private static void hideWithAnimation(JSplitPane splitPane) {
final Timer timer = new Timer(10, null);
timer.addActionListener(e -> {
splitPane.setDividerLocation(Math.max(0, splitPane.getDividerLocation() - 3));
if (splitPane.getDividerLocation() == 0)
timer.stop();
});
timer.start();
}
}
If you run it, will see that everything seems good, and the animation runs smooth.
However, in the real application the right of the JSplitPane is a JPanel with CardLayout and each card has a lot of components.
If you uncomment this line in order to simulate the number of components:
// rightPanel.add(new JLabel("s"));
and re-run the above example, you will see that the animation no longer runs smoothly. So, the question is, is is possible to make it smooth(-ier)?
I have no idea how to approach a solution - if any exists.
Based on my research, I registered a global ComponentListener:
Toolkit.getDefaultToolkit()
.addAWTEventListener(System.out::println, AWTEvent.COMPONENT_EVENT_MASK);
and saw the tons of events that are being fired. So, I think the source of the problem is the tons of component events that are being fired for each component. Also, it seems that components with custom renderers (like JList - ListCellRenderer and JTable - TableCellRenderer), component events are firing for all of the renderers. For example, if a JList has 30 elements, 30 events (component) will be fired only for it. It also seems (and that's why I mentioned it) that for CardLayout, events are taking place for the "invisible" components as well.
I know that 60*60 might sound crazy to you, but in a real application (mine has ~1500) as it makes sense, the painting is heavier.
I know that 60*60 might sound crazy to you, but in a real application (mine has ~1500) as it makes sense, the painting is heavier.
The layout manager is invoked every time the divider location is changed which would add a lot of overhead.
One solution might be to stop invoking the layout manager as the divider is animating. This can be done by overriding the doLayout() method of the right panel:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SplitPaneTest2 {
public static boolean doLayout = true;
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
JPanel leftPanel = new JPanel(new BorderLayout());
leftPanel.setBorder(BorderFactory.createLineBorder(Color.green));
JPanel rightPanel = new JPanel(new GridLayout(60, 60))
{
#Override
public void doLayout()
{
if (SplitPaneTest2.doLayout)
super.doLayout();
}
};
for (int i = 0; i < 60 * 60; i++) {
rightPanel.add(new JLabel("s"));
}
rightPanel.setBorder(BorderFactory.createLineBorder(Color.red));
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightPanel);
frame.add(splitPane);
JButton button = new JButton("Press me to hide");
button.addActionListener(e -> hideWithAnimation(splitPane));
leftPanel.add(button, BorderLayout.PAGE_START);
frame.setMaximumSize(new Dimension(800, 800));
frame.setSize(800, 800);
frame.setLocationByPlatform(true);
frame.setVisible(true);
});
}
private static void hideWithAnimation(JSplitPane splitPane) {
SplitPaneTest2.doLayout = false;
final Timer timer = new Timer(10, null);
timer.addActionListener(e -> {
splitPane.setDividerLocation(Math.max(0, splitPane.getDividerLocation() - 3));
if (splitPane.getDividerLocation() == 0)
{
timer.stop();
SplitPaneTest2.doLayout = true;
splitPane.getRightComponent().revalidate();
}
});
timer.start();
}
}
Edit:
I was not going to include my test on swapping out the panel full of components with a panel that uses an image of components since I fell the animation is the same, but since it was suggested by someone else here is my attempt for your evaluation:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.image.*;
public class SplitPaneTest2 {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
JPanel leftPanel = new JPanel(new BorderLayout());
leftPanel.setBorder(BorderFactory.createLineBorder(Color.green));
JPanel rightPanel = new JPanel(new GridLayout(60, 60));
for (int i = 0; i < 60 * 60; i++) {
rightPanel.add(new JLabel("s"));
}
rightPanel.setBorder(BorderFactory.createLineBorder(Color.red));
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightPanel);
frame.add(splitPane);
JButton button = new JButton("Press me to hide");
button.addActionListener(e -> hideWithAnimation(splitPane));
leftPanel.add(button, BorderLayout.PAGE_START);
frame.setMaximumSize(new Dimension(800, 800));
frame.setSize(800, 800);
frame.setLocationByPlatform(true);
frame.setVisible(true);
});
}
private static void hideWithAnimation(JSplitPane splitPane) {
Component right = splitPane.getRightComponent();
Dimension size = right.getSize();
BufferedImage bi = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
right.paint( g );
g.dispose();
JLabel label = new JLabel( new ImageIcon( bi ) );
label.setHorizontalAlignment(JLabel.LEFT);
splitPane.setRightComponent( label );
splitPane.setDividerLocation( splitPane.getDividerLocation() );
final Timer timer = new Timer(10, null);
timer.addActionListener(e -> {
splitPane.setDividerLocation(Math.max(0, splitPane.getDividerLocation() - 3));
if (splitPane.getDividerLocation() == 0)
{
timer.stop();
splitPane.setRightComponent( right );
}
});
timer.start();
}
}
#GeorgeZ. I think the concept presented by #camickr has to do with when you actually do the layout. As an alternative to overriding doLayout, I would suggest subclassing the GridLayout to only lay out the components at the end of the animation (without overriding doLayout). But this is the same concept as camickr's.
Although if the contents of your components in the right panel (ie the text of the labels) remain unchanged during the animation of the divider, you can also create an Image of the right panel when the user clicks the button and display that instead of the actual panel. This solution, I would imagine, involves:
A CardLayout for the right panel. One card has the actual rightPanel contents (ie the JLabels). The second card has only one JLabel which will be loaded with the Image (as an ImageIcon) of the first card.
As far as I know, by looking at the CardLayout's implementation, the bounds of all the child components of the Container are set during layoutContainer method. That would probably mean that the labels would be layed out inspite being invisible while the second card would be shown. So you should probably combine this with the subclassed GridLayout to lay out only at the end of the animation.
To draw the Image of the first card, one should first create a BufferedImage, then createGraphics on it, then call rightPanel.paint on the created Graphics2D object and finally dispose the Graphics2D object after that.
Create the second card such that the JLabel would be centered in it. To do this, you just have to provide the second card with a GridBagLayout and add only one Component in it (the JLabel) which should be the only. GridBagLayout always centers the contents.
Let me know if such a solution could be useful for you. It might not be useful because you could maybe want to actually see the labels change their lay out profile while the animation is in progress, or you may even want the user to be able to interact with the Components of the rightPanel while the animation is in progress. In both cases, taking a picture of the rightPanel and displaying it instead of the real labels while the animation takes place, should not suffice. So it really depends, in this case, on how dynamic will be the content of the rightPanel. Please let me know in the comments.
If the contents are always the same for every program run, then you could probably pre-create that Image and store it. Or even, a multitude of Images and store them and just display them one after another when the animation turns on.
Similarly, if the contents are not always the same for every program run, then you could also subclass GridLayout and precalculate the bounds of each component at startup. Then that would make GridLayout a bit faster in laying out the components (it would be like encoding a video with the location of each object), but as I am testing it, GridLayout is already fast: it just calculates about 10 variables at the start of laying out, and then imediately passes over to setting the bounds of each Component.
Edit 1:
And here is my attempt of my idea (with the Image):
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.IntBinaryOperator;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class SplitPaneTest {
//Just a Timer which plays the animation of the split pane's divider going from side to side...
public static class SplitPaneAnimationTimer extends Timer {
private final JSplitPane splitPane;
private int speed, newDivLoc;
private IntBinaryOperator directionf;
private Consumer<SplitPaneAnimationTimer> onFinish;
public SplitPaneAnimationTimer(final int delay, final JSplitPane splitPane) {
super(delay, null);
this.splitPane = Objects.requireNonNull(splitPane);
super.setRepeats(true);
super.setCoalesce(false);
super.addActionListener(e -> {
splitPane.setDividerLocation(directionf.applyAsInt(newDivLoc, splitPane.getDividerLocation() + speed));
if (newDivLoc == splitPane.getDividerLocation()) {
stop();
if (onFinish != null)
onFinish.accept(this);
}
});
speed = 0;
newDivLoc = 0;
directionf = null;
onFinish = null;
}
public int getSpeed() {
return speed;
}
public JSplitPane getSplitPane() {
return splitPane;
}
public void play(final int newDividerLocation, final int speed, final IntBinaryOperator directionf, final Consumer<SplitPaneAnimationTimer> onFinish) {
if (newDividerLocation != splitPane.getDividerLocation() && Math.signum(speed) != Math.signum(newDividerLocation - splitPane.getDividerLocation()))
throw new IllegalArgumentException("Speed needs to be in the direction towards the newDividerLocation (from the current position).");
this.directionf = Objects.requireNonNull(directionf);
newDivLoc = newDividerLocation;
this.speed = speed;
this.onFinish = onFinish;
restart();
}
}
//Just a GridLayout subclassed to only allow laying out the components only if it is enabled.
public static class ToggleGridLayout extends GridLayout {
private boolean enabled;
public ToggleGridLayout(final int rows, final int cols) {
super(rows, cols);
enabled = true;
}
#Override
public void layoutContainer(final Container parent) {
if (enabled)
super.layoutContainer(parent);
}
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}
}
//How to create a BufferedImage (instead of using the constructor):
private static BufferedImage createBufferedImage(final int width, final int height, final boolean transparent) {
final GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
final GraphicsDevice gdev = genv.getDefaultScreenDevice();
final GraphicsConfiguration gcnf = gdev.getDefaultConfiguration();
return transparent
? gcnf.createCompatibleImage(width, height, Transparency.TRANSLUCENT)
: gcnf.createCompatibleImage(width, height);
}
//This is the right panel... It is composed by two cards: one for the labels and one for the image.
public static class RightPanel extends JPanel {
private static final String CARD_IMAGE = "IMAGE",
CARD_LABELS = "LABELS";
private final JPanel labels, imagePanel; //The two cards.
private final JLabel imageLabel; //The label in the second card.
private final int speed; //The speed to animate the motion of the divider.
private final SplitPaneAnimationTimer spat; //The Timer which animates the motion of the divider.
private String currentCard; //Which card are we currently showing?...
public RightPanel(final JSplitPane splitPane, final int delay, final int speed, final int rows, final int cols) {
super(new CardLayout());
super.setBorder(BorderFactory.createLineBorder(Color.red));
spat = new SplitPaneAnimationTimer(delay, splitPane);
this.speed = Math.abs(speed); //We only need a positive (absolute) value.
//Label and panel of second card:
imageLabel = new JLabel();
imageLabel.setHorizontalAlignment(JLabel.CENTER);
imageLabel.setVerticalAlignment(JLabel.CENTER);
imagePanel = new JPanel(new GridBagLayout());
imagePanel.add(imageLabel);
//First card:
labels = new JPanel(new ToggleGridLayout(rows, cols));
for (int i = 0; i < rows * cols; ++i)
labels.add(new JLabel("|"));
//Adding cards...
final CardLayout clay = (CardLayout) super.getLayout();
super.add(imagePanel, CARD_IMAGE);
super.add(labels, CARD_LABELS);
clay.show(this, currentCard = CARD_LABELS);
}
//Will flip the cards.
private void flip() {
final CardLayout clay = (CardLayout) getLayout();
final ToggleGridLayout labelsLayout = (ToggleGridLayout) labels.getLayout();
if (CARD_LABELS.equals(currentCard)) { //If we are showing the labels:
//Disable the laying out...
labelsLayout.setEnabled(false);
//Take a picture of the current panel state:
final BufferedImage pic = createBufferedImage(labels.getWidth(), labels.getHeight(), true);
final Graphics2D g2d = pic.createGraphics();
labels.paint(g2d);
g2d.dispose();
imageLabel.setIcon(new ImageIcon(pic));
imagePanel.revalidate();
imagePanel.repaint();
//Flip the cards:
clay.show(this, currentCard = CARD_IMAGE);
}
else { //Else if we are showing the image:
//Enable the laying out...
labelsLayout.setEnabled(true);
//Revalidate and repaint so as to utilize the laying out of the labels...
labels.revalidate();
labels.repaint();
//Flip the cards:
clay.show(this, currentCard = CARD_LABELS);
}
}
//Called when we need to animate fully left motion (ie until reaching left side):
public void goLeft() {
final JSplitPane splitPane = spat.getSplitPane();
final int currDivLoc = splitPane.getDividerLocation(),
minDivLoc = splitPane.getMinimumDividerLocation();
if (CARD_LABELS.equals(currentCard) && currDivLoc > minDivLoc) { //If the animation is stopped:
flip(); //Show the image label.
spat.play(minDivLoc, -speed, Math::max, ignore -> flip()); //Start the animation to the left.
}
}
//Called when we need to animate fully right motion (ie until reaching right side):
public void goRight() {
final JSplitPane splitPane = spat.getSplitPane();
final int currDivLoc = splitPane.getDividerLocation(),
maxDivLoc = splitPane.getMaximumDividerLocation();
if (CARD_LABELS.equals(currentCard) && currDivLoc < maxDivLoc) { //If the animation is stopped:
flip(); //Show the image label.
spat.play(maxDivLoc, speed, Math::min, ignore -> flip()); //Start the animation to the right.
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
JPanel leftPanel = new JPanel(new BorderLayout());
leftPanel.setBorder(BorderFactory.createLineBorder(Color.green));
int rows, cols;
rows = cols = 60;
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
final RightPanel rightPanel = new RightPanel(splitPane, 10, 3, rows, cols);
splitPane.setLeftComponent(leftPanel);
splitPane.setRightComponent(rightPanel);
JButton left = new JButton("Go left"),
right = new JButton("Go right");
left.addActionListener(e -> rightPanel.goLeft());
right.addActionListener(e -> rightPanel.goRight());
final JPanel buttons = new JPanel(new GridLayout(1, 0));
buttons.add(left);
buttons.add(right);
frame.add(splitPane, BorderLayout.CENTER);
frame.add(buttons, BorderLayout.PAGE_START);
frame.setSize(1000, 800);
frame.setMaximumSize(frame.getSize());
frame.setLocationByPlatform(true);
frame.setVisible(true);
splitPane.setDividerLocation(0.5);
});
}
}
Hi I am here add delay BETWEEN the formation of each arc of a Rainbow using Thread.delay() so that it look like an animation. When I am using Thread.delay() it delays the whole process. Is there any other method or I am doing it wrong. Please help me solve the problem
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.*;
import javax.swing.*;
public class RainBow2{
static int x,y,z;
static RainBowPanel rainBow = new RainBowPanel();
static StdRainBow stdRainBow = new StdRainBow();
static JTextField xCo = new JTextField(4);
static JTextField yCo = new JTextField(4);
static JTextField angle = new JTextField(4);
static JFrame frame;
public static void main(String[] args){
Color color = new Color(135,206,250);
frame = new JFrame("Rainbow");
JPanel panel = new JPanel();
JButton draw = new JButton("Draw RainBow");
draw.addActionListener(new ListenButton());
JLabel lab1 = new JLabel("X-Coordinate");
JLabel spaceLab = new JLabel(" ");
JLabel lab2 = new JLabel("Y-Coordinate");
JLabel angleLab = new JLabel("Initial Angle");
JButton chButton = new JButton("Change Color");
chButton.addActionListener(new ListenButton());
panel.setBackground(color);
panel.add(angleLab);
panel.add(angle);
panel.add(lab1);
panel.add(xCo);
panel.add(spaceLab);
panel.add(lab2);
panel.add(yCo);
panel.add(draw);
panel.add(spaceLab);
panel.add(chButton);
frame.getContentPane().add(BorderLayout.SOUTH,panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1366,740);
frame.setVisible(true);
rainBow.addMouseListener(new RainBowList());
frame.getContentPane().add(rainBow);
}
static class RainBowList extends MouseAdapter implements MouseMotionListener{
public void mouseClicked(MouseEvent e){
x = e.getX();
y = e.getY();
rainBow.drawing(x, y, 0);
}
}
static class ListenButton implements ActionListener{
public void actionPerformed(ActionEvent a){
if(a.getActionCommand().equals("Draw RainBow")){
x = Integer.parseInt(xCo.getText());
y = Integer.parseInt(yCo.getText());
z = Integer.parseInt(angle.getText());
rainBow.drawing(x, y,z);
}
if(a.getActionCommand().equals("Change Color")){
rainBow.drawing(x, y,z);
}
}
}
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;
public class RainBowPanel extends JPanel{
int x,y,z;
public void drawing(int xx, int yy, int zz){
Color color = new Color(135,206,250);
setBackground(color);
z = zz;
x = xx;
y = yy;
repaint();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
int length = 300;
int width = 300;
x = x-length/2;
y = y-width/2;
for(int i =0 ; i< 7;i++){
Color color = new Color((int)(Math.random()*255),(int)(Math.random()*255),(int)(Math.random()*255));
g.setColor(color);
g.fillArc(x,y,length ,width ,z,180 );
x=x+15;
y=y+15;
length = (length-30);
width = (width-30);
try{
Thread.sleep(200);
}catch(Exception e){
}
}
}
}
}
When I am using Thread.delay() it delays the whole process.
A painting method is for painting only. You should not be causing the Thread to delay. This will prevent the GUI from repainting itself until the entire loop is finished executing.
Instead you can use a Swing Timer to schedule the repainting.
Instead of doing the painting in the paintComponent() you should maybe paint to a BufferedImage. Then you can display the Image in an ImageIcon on a JLabel.
So when the Timer event is generated you paint a color of the rainbow. After all 7 colors are painted you stop the Timer.
Read the section from the Swing tutorial on How to Use Swing Timers for more information and examples.
EDIT: leaving short code which shows issue:
Then label.setText("") is called and it has to change the text, i.e. scaler overcame 50 barrier the label dissapears for the mousewheelevent -> re-emerges on next event. Also a problem of the label not being present on the Load-up.
In actual program labels stack together in 1 spot if any of them has .setText() changing value or .setVisible changing (These two methods I need to be able to use without affecting labels positioning)
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.OverlayLayout;
public class brokenLabel extends JFrame
{
private JLayeredPane layerPane;
private JPanel breakingLabelPanel = new JPanel();
private JLabel label;
private int scaler = 50;
private int xstart = 200;
private int ystart = 200;
public static void main(String[] args)
{
#SuppressWarnings("unused")
brokenLabel program = new brokenLabel();
}
public brokenLabel()
{
// Frame setup used on the program
final JFrame frame = new JFrame("This is a slideshow");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
layerPane = new JLayeredPane();
frame.setContentPane(layerPane);
// Panel used (it's similar to what's added on the actual program)
breakingLabelPanel.setOpaque(true);
breakingLabelPanel.setLayout(new OverlayLayout(breakingLabelPanel));
breakingLabelPanel.setVisible(true);
breakingLabelPanel.setSize(getMaximumSize());
breakingLabelPanel.addMouseWheelListener(new MouseWheelListener(){
#Override
public void mouseWheelMoved(MouseWheelEvent arg0)
{
if (scaler > 5 || arg0.getWheelRotation() < 0)
scaler = scaler - 5*arg0.getWheelRotation();
setTexts();
}
});
//The testing label
label = new JLabel(new String("1"));
label.setLocation(xstart , ystart);
breakingLabelPanel.add(label);
frame.add(breakingLabelPanel,layerPane);
frame.setVisible(true);
//revalidate and repaint don't help make label visible on load-up
frame.revalidate();
frame.repaint();
}
public void setTexts(){
label.setLocation(xstart + scaler,ystart + 25);
if(scaler < 50)
{
label.setText("1");
}
else
{
label.setText("2");
}
}
}
I'm trying to resize a window dynamically using a Timer object, but not succeeding... I set the preferred size of the panel in the constructor, which sets the size of the window nicely, though only once. The preferred size changes after the program is initialized, but the window size stays the same. Why? Because the constructor is initialized only once and therefore isn't affected by the size change? If so, how could I get around this to resize the window in real-time?
I know this won't solve the problem in the exercise given in the beginning comments, so please ignore that :-)
/*
* Exercise 18.15
*
* "(Enlarge and shrink an image) Write an applet that will display a sequence of
* image files in different sizes. Initially, the viewing area for this image has
* a width of 300 and a height of 300. Your program should continuously shrink the
* viewing area by 1 in width and 1 in height until it reaches a width of 50 and
* a height of 50. At that point, the viewing area should continuously enlarge by
* 1 in width and 1 in height until it reaches a width of 300 and a height of 300.
* The viewing area should shrink and enlarge (alternately) to create animation
* for the single image."
*
* Created: 2014.01.07
*/
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Ex_18_15 extends JApplet {
// Main method
public static void main(String[] args) {
JFrame frame = new JFrame();
Ex_18_15 applet = new Ex_18_15();
applet.isStandalone = true;
frame.add(applet);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
// Data fields
private boolean isStandalone = false;
private Image image = new ImageIcon("greenguy.png").getImage();
private int xCoordinate = 360;
private int yCoordinate = 300;
private Timer timer = new Timer(20, new TimerListener());
private DrawPanel panel = new DrawPanel();
// Constructor
public Ex_18_15() {
panel.setPreferredSize(new Dimension(xCoordinate, yCoordinate));
add(panel);
timer.start();
}
class DrawPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
}
class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if(yCoordinate <= 50) {
yCoordinate++;
xCoordinate++;
}
else if(yCoordinate >= 300) {
yCoordinate--;
xCoordinate--;
}
panel.setPreferredSize(new Dimension(xCoordinate, yCoordinate));
repaint();
}
}
}
You need to re-pack your JFrame to resize it. For instance at the end of your ActionListener:
Window win = SwingUtilities.getWindowAncestor(panel);
win.pack();
A question for you though: Why in heaven's name is your class extending JApplet and not JPanel? Or if it needs to be an applet, why are you stuffing it into a JFrame?
Edit
Regarding your comment:
Wouldn't it usually be extending JFrame not JPanel? I'm stuffing it into a JFrame to allow it to run as an application as well as an applet. That's how 'Introduction to Java Programming' tells me how to do it :p Adding your code at the end of the actionPerformed method didn't do anything for me ;o
Most of your GUI code should be geared towards creating JPanels, not JFrames or JApplets. You can then place your JPanels where needed and desired without difficulty. Your book has serious issues and should not be trusted if it is telling you this.
Edit 2
Works for me:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class ShrinkingGui extends JPanel {
private static final int INIT_W = 400;
private static final int INIT_H = INIT_W;
private static final int TIMER_DELAY = 20;
private int prefW = INIT_W;
private int prefH = INIT_H;
public ShrinkingGui() {
new Timer(TIMER_DELAY, new TimerListener()).start();;
}
public Dimension getPreferredSize() {
return new Dimension(prefW, prefH);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (prefW > 0 && prefH > 0) {
prefW--;
prefH--;
Window win = SwingUtilities.getWindowAncestor(ShrinkingGui.this);
win.pack();
} else {
((Timer)e.getSource()).stop();
}
}
}
private static void createAndShowGUI() {
ShrinkingGui paintEg = new ShrinkingGui();
JFrame frame = new JFrame("Shrinking Gui");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(paintEg);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
I am writing a small program that converts files, and I wanted to have a box pop up that asks the user to please wait while the program loops through and converts all the relevant files, but I am running into a small problem. The box that pops up should have a JLabel and a JButton, while the user is "waiting" I wanted to display a message that says please wait, and a disabled "OK" JButton, and then when its finished I wanted to set the text of the JLabel to let them know that It successfully converted their files, and give them a count of how many files were converted. (I wrote a method called alert that sets the text of the label and enables the button.) The problem is That while the program is running, the box is empty, the Label and the Button are not visible, when it finishes, label appears with the final text that I want and the button appears enabled. I am not sure exactly what is going on, I tried changing the modifiers of the JLabel and JButton several times but I cant seem to get it to work correctly. Here is the code for the box that pops up, any help is greatly appricated.
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class PleaseWait extends javax.swing.JFrame{
private static final int height = 125;
private static final int width = 350;
final static JLabel converting = new JLabel("Please Wait while I convert your files");
private static JButton OK = new JButton("OK");
public PleaseWait(){
// creates the main window //
JFrame mainWindow = new JFrame();
mainWindow.setTitle("Chill For A Sec");
mainWindow.setSize(width, height);
mainWindow.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
// creates the layouts//
JPanel mainLayout = new JPanel(new BorderLayout());
JPanel textLayout = new JPanel(new FlowLayout());
JPanel buttonLayout = new JPanel(new FlowLayout());
// Sets Text //
converting.setText("Please wait while I convert your files");
// disables button //
OK.setEnabled(false);
// adds to the layouts //
textLayout.add(converting);
buttonLayout.add(OK);
mainLayout.add(textLayout, BorderLayout.CENTER);
mainLayout.add(buttonLayout, BorderLayout.SOUTH);
// adds to the frame //
mainWindow.add(mainLayout);
// sets everything visible //
mainWindow.setVisible(true);
}
public static void alert(){
OK.setEnabled(true);
String total = String.valueOf(Convert.result());
converting.setText("Sucsess! " + total + " files Converted");
}
}
Okay here's the issue. You are extending the JFrame . That means your class IS a JFrame.
When you create the PleaseWait frame you don't do anything to it. This is the empty box you are seeing. You are instead creating a different JFrame in your constructor. Remove your mainWindow and instead just use this. Now all of your components will be added to your PleaseWait object. That should fix your blank box issue.
You need an application to create your frame first. This is a simple example of such application.
import javax.swing.UIManager;
import java.awt.*;
public class Application {
boolean packFrame = false;
//Construct the application
public Application() {
PleaseWait frame = new PleaseWait();
//Validate frames that have preset sizes
//Pack frames that have useful preferred size info, e.g. from their layout
if (packFrame) {
frame.pack();
}
else {
frame.validate();
}
//Center the window
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = frame.getSize();
if (frameSize.height > screenSize.height) {
frameSize.height = screenSize.height;
}
if (frameSize.width > screenSize.width) {
frameSize.width = screenSize.width;
}
frame.setLocation((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2);
frame.setVisible(true);
frame.convert();
}
//Main method
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch(Exception e) {
e.printStackTrace();
}
new Application();
}
}
You have to slightly modify your frame to add controls to the content pane. You can do some work after frame is created, then call alert.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class PleaseWait extends JFrame {
private static final int height = 125;
private static final int width = 350;
final static JLabel converting = new JLabel();
private static JButton OK = new JButton("OK");
BorderLayout borderLayout1 = new BorderLayout();
JPanel contentPane;
int count;
public PleaseWait(){
contentPane = (JPanel)this.getContentPane();
contentPane.setLayout(borderLayout1);
this.setSize(new Dimension(width, height));
this.setTitle("Chill For A Sec");
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
// creates the layouts//
JPanel mainLayout = new JPanel(new BorderLayout());
JPanel textLayout = new JPanel(new FlowLayout());
JPanel buttonLayout = new JPanel(new FlowLayout());
// Sets Text //
converting.setText("Please wait while I convert your files");
// disables button //
OK.setEnabled(false);
OK.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
// adds to the layouts //
textLayout.add(converting);
buttonLayout.add(OK);
mainLayout.add(textLayout, BorderLayout.CENTER);
mainLayout.add(buttonLayout, BorderLayout.SOUTH);
// adds to the frame //
contentPane.add(mainLayout);
}
public void convert(){
count = 0;
for (int i = 0; i <10; i++){
System.out.println("Copy "+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
count++;
}
alert();
}
public void alert(){
OK.setEnabled(true);
// String total = String.valueOf(Convert.result());
converting.setText("Sucsess! " + count + " files Converted");
}
}