I am having trouble with a game I am making. I am attempting to fade a JLabel, using a Timer. So far, I have been able to set a JLabel as transparent, and I have been able to make a working Timer. But somehow, I am not able to combine them together. I can set a JLabel's background to
l.setBackground(new Color(0, 0, 0, 200));
(l being desired JLabel to change the color of, of course)
and it works. The JLabel is transparent.
But I have not been able to put it into a timer so that the color fades:
public static void fadeLabel(JLabel lab, int steps, int target) {
class fade extends Thread {
public void run() {
Timer t = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(lab.getBackground().getAlpha());
lab.setBackground(new
Color(lab.getBackground().getRed(),
lab.getBackground().getGreen(),
lab.getBackground().getBlue(),
lab.getBackground().getAlpha() + steps));
if (lab.getBackground().getAlpha() == target) {
((Timer) e.getSource()).stop();
}
}
});
t.start();
}
}
new Thread(new fade()).start();
}
instead, it just goes from black to transparent. Nothing in-between. It also throws the error java.lang.IllegalArgumentException: Color parameter outside of expected range: Alpha. However, I have set the step to -5 (the rate at which it fades, in this case, fades out, as it is negative) and if you remember, I set the alpha to something that is divisible by five, and I also have a checker for when it gets to the target, so that it stops the Timer when it reaches that target. Is there something I'm doing wrong? Is the JLabel's background updating too fast for it to show? Any help would be great.
Edit:
I have fixed the java.lang.IllegalArgumentException: Color parameter outside of expected range: Alpha. But the other one about the JLabel not fading is still there, and I don't know how to fix it.
I'm not directly answering your question. A wrote some code, take a look :D
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class MyWindow extends JFrame {
public MyWindow() {
initComponents();
}
private void initComponents() {
setTitle( "Fading labels :)" );
setDefaultCloseOperation( EXIT_ON_CLOSE );
setSize( 400, 400 );
setLocationRelativeTo( null );
setLayout( new FlowLayout() );
for ( int i = 0; i < 550; i++ ) {
JLabel lab = new JLabel( "x" );
add( lab );
fadeLabel( lab, 10, i / 10, 0 );
}
}
private void fadeLabel( JLabel label, int steps, int skipSteps, int targetAlpha ) {
new LabelFader( label, steps, skipSteps, targetAlpha ).start();
}
private class LabelFader {
JLabel label;
int steps;
int skipSteps;
int targetAlpha;
LabelFader( JLabel label, int steps, int skipSteps, int targetAlpha ) {
this.label = label;
this.steps = steps;
this.skipSteps = skipSteps;
this.targetAlpha = targetAlpha;
}
void start() {
Color startColor = label.getForeground();
int startAlpha = startColor.getAlpha();
int deltaAlpha = startAlpha - targetAlpha;
if ( deltaAlpha > 0 ) {
int alphaStep = deltaAlpha / steps;
new Thread( new Runnable() {
#Override
public void run() {
int totalIterations = steps + skipSteps;
int currIteration = 1;
for ( int i = 0; i < totalIterations; i++ ) {
if ( skipSteps == 0 ) {
int newAlpha = startAlpha - alphaStep * currIteration++;
newAlpha = newAlpha < 0 ? 0 : newAlpha;
Color newColor = new Color(
startColor.getRed(),
startColor.getGreen(),
startColor.getGreen(),
newAlpha );
label.setForeground( newColor );
} else {
skipSteps--;
}
try {
Thread.sleep( 100 );
} catch ( InterruptedException exc ) {
exc.printStackTrace();
}
}
Color newColor = new Color(
startColor.getRed(),
startColor.getGreen(),
startColor.getGreen(),
targetAlpha );
label.setForeground( newColor );
}
}).start();
}
}
}
public static void main( String[] args ) {
EventQueue.invokeLater( new Runnable() {
public void run() {
new MyWindow().setVisible( true );
}
});
}
}
A JLabel is not opaque by default, so its background is not painted. Therefore changing its background color will have no effect - unless you explicitly made your JLabel opaque by calling the relevant method, as in
lab.setOpaque(true)
Forgive me but I couldn't find that in the code you posted.
Also, code that affects the GUI needs to be performed on the Event Dispatch Thread (EDT). Your code that changes the JLabel background color is being run in a separate thread. The result of running that code is therefore unpredictable. The javax.swing.Timer class does not need to be run in a separate thread. Simply create a Timer object and then call its start method. So just take your code that creates the Timer out of the Thread and put it in with the rest of your GUI code.
If you would like a small, demo program showing how to fade the text of a JLabel, let me know.
EDIT: Here is the demo that fades the JLabel text. A JFrame containing a JLabel and a JButton. Click the JButton and the JLabel text fades until it disappears.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import javax.swing.WindowConstants;
/**
* Tests <i>Swing</i> {#link javax.swing.Timer timer}.
*/
public class LabelDim {
private int alpha;
private JButton button;
private JFrame frame;
private JLabel label;
private Timer timer;
public LabelDim() {
timer = new Timer(200, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (alpha > 0) {
alpha -= 15;
label.setForeground(new Color(0, 0, 0, alpha));
}
else {
timer.stop();
}
}
});
}
private void showGui() {
frame = new JFrame("Dim");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
label = new JLabel("I am a fading label.", SwingConstants.CENTER);
alpha = label.getForeground().getAlpha();
label.setFont(label.getFont().deriveFont(24.0f));
label.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20));
frame.add(label, BorderLayout.CENTER);
button = new JButton("Start");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
JPanel panel = new JPanel();
panel.add(button);
frame.add(panel, BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
LabelDim instance = new LabelDim();
EventQueue.invokeLater(() -> instance.showGui());
}
}
SECOND EDIT:
Here's some sample code that works. It'll throw the Exception you've already fixed but this is just for demonstration purposes only :).
Play with the label.setOpaque and its background and tell what happens with yourself. If you need to draw the label background set that flag to true.
If you do not need to draw its background do not modify label's background and do not set it to be opaque and modifying the foreground color should be more than enough! :)
package main;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Main extends JFrame {
JLabel label;
JButton btn;
public Main() {
setSize(500,500);
setLayout(new FlowLayout());
label = new JLabel("My super awesome label");
//label.setOpaque(true); //Play with these lines and see what happens
//label.setBackground(Color.BLUE); //Play with these lines and see what happens
btn = new JButton("Click me");
btn.setSize(200,200);
btn.addActionListener(
(ActionEvent evt) -> {
Timer t = new Timer(100,
(ActionEvent event) -> {
Color foreground = label.getForeground();
Color background = label.getBackground();
foreground = decreaseAlpha(foreground);
background = decreaseAlpha(background);
label.setForeground(foreground);
label.setBackground(background);
}
);
t.start();
}
);
this.add(label);
this.add(btn);
}
public static void main(String[] args) {
(new Main()).setVisible(true);
}
public Color decreaseAlpha(Color original) {
return new Color(original.getRed(), original.getGreen(), original.getBlue(), original.getAlpha() - 10);
}
}
EDIT:
You might be changing your background of your label instead of its foreground...
Timer t = new Timer(10, (ActionEvent evt)-> {
l.setForeground(new
Color(l.getForeground().getRed(),
l.getForeground().getGreen(),
l.getForeground().getBlue(),
l.getForeground().getAlpha() - 1)
);
l.invalidate();
});
t.start();
Being l your label
Also, don't do this:
new Thread(new fade()).start();
Your fade class is already a Thread because it's extending it. Use (new fade()).start(); instead
Related
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
As stated in the above comments (by #Sergiy Medvynskyy) blocking the Swing Thread caused it to not render correctly. With using a Swing Timer the animation works smoothly. The code I have used for the solution is:
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();
The above code is used in place of the while loop in my original question. Thanks to Sergiy Medvynskyy for the answer.
I wanted to create a JFrame and put a sequence of images for animation in there. But the images don't appear in the frame window. I just want basic troubleshooting tips to make it appear in the window. Just edit the code for an answer if you can.
My question:
Why isn't the window displaying any pictures? It shows a window with a background color of blue, but that's it. Please tell me an efficient way to store images in variables and display it in a loop.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.io.*;
public class Game extends JLabel implements ActionListener{
/**
*
*/
private static final long serialVersionUID = 1L;
public static Game blah;
BufferedImage nekopics[] = new BufferedImage[7];
BufferedImage currentimg;
public String nekosrcs[];
int xpos;
Timer timer;
public Game() throws IOException
{
JFrame jframe = new JFrame();
nekosrcs = new String[] { "walk1.png", "walk2.png",
"walk3.png", "walk4.png", "walk5.png",
"walk6.png"};
jframe.setTitle("Game");
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setLayout(new FlowLayout());
jframe.setSize(400, 400);
jframe.setResizable(false);
jframe.setVisible(true);
jframe.getContentPane().setBackground(Color.BLUE);
for (int i=0; i < nekopics.length; i++) {
nekopics[i] = ImageIO.read(new FileInputStream("D:/Programs
/pics"+nekosrcs[i]));
}
for (int i=0; i < nekopics.length; i++) {
timer = new Timer(1000, this);
timer.setInitialDelay(0);
timer.start();
currentimg = nekopics[i];
repaint();
}
}
public void paintComponent(Graphics g)
{
super.paint(g);
g.drawImage(currentimg,100,100,this);
}
public static void main(String[] args) throws IOException {
blah = new Game();
}
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
}
Alright, there are a lot of problems in your code, let's step into each of them:
You have a lot of spaces between lines, that makes your code a lot larger and harder to read
You haven't indented your code correctly (see the last } on your code, it's at the same level than the others; your for loops, etc), it makes the code so much harder to read and understand as well
You're creating a JFrame but extending JLabel, I'm not sure why you're doing this, if you're doing it so you can use the paintComponent() method, it's not necessary, on my code you can see how you can do it w/o extending any Component
If you haven't read the Swing Timer docs you should click that link and read what the ActionListener parameter does. In this case, we're going to use this listener to call the repaint() method and update our currentImage (or nextImage in the code below) and change the image accordingly. You failed to do this.
You were creating more than 1 Timer too, you created 6 here! All of them new but they had no action to do when the time finished
for (int i=0; i < nekopics.length; i++) {
timer = new Timer(1000, this);
timer.setInitialDelay(0);
timer.start();
currentimg = nekopics[i];
repaint();
}
You're changing unnecessarily the visibility of the paintComponent() method to public from protected
However I want to congratulate you for not using a null layout and following the recommendations I made on the comments above.
And finally the code that changes one image for another inside a Timer is the following, you can copy-paste it and change the image's names so you can see how it works.
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileInputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class ImageSequence {
private JFrame frame;
private JPanel pane;
private Timer timer;
private int nextImage = 0;
private String[] images = {"tokyo", "tokyo2", "starwars"};
private Image img = null;
public static void main (String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ImageSequence().createAndShowGui();
}
});
}
public void createAndShowGui() {
frame = new JFrame("Image Sequence");
timer = new Timer(1000, listener);
pane = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
try {
img = ImageIO.read(new FileInputStream("/home/jesus/Pictures/" + images[nextImage] + ".jpg"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
g.drawImage(img , 0, 0, 200, 200, this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
};
timer.start();
frame.getContentPane().add(pane);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
ActionListener listener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
System.out.println(nextImage);
nextImage = nextImage < images.length - 1 ? nextImage + 1 : 0;
System.out.println(nextImage);
pane.repaint();
}
};
}
I need to create a 4 x 4 grid of rectangles in Java, I then need these rectangles to change color in a sequence.
I've never done any graphical work before, just things in the console.
I started by doing some research and created a 650 x 650 JFrame to put the rectangles in.
After that I used GridLayout and managed to create a 4 x 4 grid out of buttons using window.JButton which wasn't right.
How would I create rectangles instead? And would it be right to use for loops with ++ to time the animation?
I couldn't find anything that worked for my needs when searching on stackoverflow and google. Sorry if this is a stupid question. I'm new to this and I'm doing for an apprecticeship.
Here's how I would like it to look like, with each rectangle changing color in a time interval
From #Eng.Fouad answer (so give him credit and upvote his answer too), I made some changes, this example shows how to use a Swing Timer which changes color every second from green to red. I'm using a simple JLabel for demonstration purposes, take this logic into the GridLayout you have:
Here are some screen shots on how it looks:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.Color;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class SimpleTimer extends JFrame
{
private JLabel label;
private Timer timer;
private int counter = 3; // the duration
private int delay = 1000; // every 1 second
private static final long serialVersionUID = 1L;
private Color c = Color.RED;
private boolean red = true;
private boolean stop = false;
int i = counter;
public SimpleTimer()
{
super("Simple Timer");
setDefaultCloseOperation(EXIT_ON_CLOSE);
label = new JLabel("Wait for " + counter + " sec", JLabel.CENTER);
JPanel contentPane = (JPanel) getContentPane();
contentPane.add(label, BorderLayout.CENTER);
contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
pack();
timer = new Timer(delay, action);
timer.setInitialDelay(0);
timer.start();
setVisible(true);
}
ActionListener action = new ActionListener()
{
#Override
public void actionPerformed(ActionEvent event)
{
if(i == 0)
{
timer.stop();
stop = true;
i = counter;
timer = new Timer(delay, action);
timer.setInitialDelay(0);
timer.start();
}
else
{
c = red ? Color.GREEN : Color.RED;
red = !red;
label.setBackground(c);
label.setOpaque(true);
label.setText("Wait for " + i + " sec");
i--;
}
}
};
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new SimpleTimer();
}
});
}
}
You could use jLabel, and set background color to it. How you do it, you can read here: How do I set a JLabel's background color?
Then just use for loop and Thred.sleep to change there color in animation.
(Beginner)
Hi, sorry for the specific question, but I'm having errors constantly ambush me out of nowhere with a program that I would expect to be quite simple.
I was planning on creating a program that would allow the user to click on JPanels with in a GridLayout in order to change their colours. Imagine a poor man's pixel art program, like the old MS Paint.
The plan was to create a JFrame set to GridLayout, of an integer width and height, and fill the grids with JPanels with a 2d array and a for loop. I would then put a MouseListener into each individual JPanel to listen for a mouseClicked, which would change the background colour of the panel clicked.
package pixelpainter;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
public class PixelPainter extends JPanel {
int width = 20;
int height = 20;
int pixSize = 10;
Color bGColor = Color.WHITE;
Dimension pixDim = new Dimension(pixSize,pixSize);
private JPanel panelClicked = null;
JFrame frame= new JFrame();
/**
* #param args the command line arguments
*/
public PixelPainter()
{
initGUI();
}
public void initGUI() {
frame.setLayout(new GridLayout(height, width, 0, 0));
frame.setSize((height * pixSize), (width * pixSize));
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
int[][] pixGrid = new int [width][height];
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
JPanel pixel[][] = new JPanel[width][height];
frame.add(pixel[row][col]);
pixel[row][col].setBackground(bGColor);
pixel[row][col].setPreferredSize(pixDim);
pixel[row][col].setBorder(BorderFactory.createLineBorder(Color.BLACK));
pixel[row][col].addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent click)
{
JPanel selectedPixel = (JPanel) getComponentAt(click.getPoint());
if (selectedPixel == null || selectedPixel == PixelPainter.this)
{
return;
}
if (selectedPixel != null)
{
selectedPixel.setBackground(Color.BLACK);
}
}
#Override
public void mousePressed(MouseEvent press)
{
}
});
}
}
}
public static void main(String[] args){
EventQueue.invokeLater(new Runnable(){
#Override
public void run(){
new PixelPainter().setVisible(true);
}
});
}
}
Ideally I would be using the 2d array JFrame when filling in the colours, but apparently they must be final or effectively final.
I rearranged your code to group like things together.
Here's the GUI I created.
I made the following changes to your code.
I had the main class implement Runnable. Since the EventQueue invokeLater method expects a Runnable, you might as well make the main class a Runnable.
I moved the JPanel creation into the createPixels method. Your methods should do one thing and do that one thing well.
The initGUI method now just creates the JFrame.
I moved the sizing integers into the new PixelPanel class. The class that extends a JPanel has to provide a preferred size. The JFrame pack method then creates a JFrame of the correct size.
In the paintComponent method of the PixelPanel class, all I do is paint. You shouldn't do anything else but paint in the paintComponent method.
I made the pixels bigger, so I could left click and right click on a pixel easier. The left click makes the pixel blue, and the right click erases the blue (makes the pixel white).
Because of the model / view / controller pattern, I pulled the mouse adapter code into its own class. Separating the concerns makes getting each part of the GUI working properly much easier.
And here's the code.
package com.ggl.testing;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PixelPainter implements Runnable {
private JFrame frame;
public static void main(String[] args) {
EventQueue.invokeLater(new PixelPainter());
}
#Override
public void run() {
initGUI();
}
public void initGUI() {
frame = new JFrame("Pixel Art");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createPixels());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createPixels() {
int width = 30;
int height = 20;
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(height, width, 0, 0));
for (int row = 0; row < height; row++) {
for (int column = 0; column < width; column++) {
PixelPanel pixelPanel = new PixelPanel();
pixelPanel.addMouseListener(new ColorListener(pixelPanel));
panel.add(pixelPanel);
}
}
return panel;
}
public class PixelPanel extends JPanel {
private static final long serialVersionUID = 8465814529701152253L;
private static final int PIXEL_SIZE = 20;
private Color backgroundColor;
public PixelPanel() {
this.backgroundColor = Color.WHITE;
this.setBorder(BorderFactory.createLineBorder(Color.BLACK));
this.setPreferredSize(new Dimension(PIXEL_SIZE, PIXEL_SIZE));
}
public Color getBackgroundColor() {
return backgroundColor;
}
public void setBackgroundColor(Color backgroundColor) {
this.backgroundColor = backgroundColor;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(getBackgroundColor());
g.fillRect(0, 0, getWidth(), getHeight());
}
}
public class ColorListener extends MouseAdapter {
private PixelPanel panel;
public ColorListener(PixelPanel panel) {
this.panel = panel;
}
#Override
public void mousePressed(MouseEvent event) {
if (event.getButton() == MouseEvent.BUTTON1) {
panel.setBackgroundColor(Color.BLUE);
panel.repaint();
} else if (event.getButton() == MouseEvent.BUTTON3) {
panel.setBackgroundColor(Color.WHITE);
panel.repaint();
}
}
}
}
Your code is creating a new pixel Array inside the loop. The idea is to create the Array out side the loo and then create a new JPanel to add to the Array inside the loop.
Something like:
int[][] pixGrid = new int [width][height];
JPanel pixel[][] = new JPanel[width][height];
and
//JPanel pixel[][] = new JPanel[width][height];
pixel[row][col] = new JPanel();
Now inside the listener because you add the listener to every panel you can access the panel directly without worrying about the mouse point:
//JPanel selectedPixel = (JPanel) getComponentAt(click.getPoint());
JPanel selectedPixel = (JPanel)click.getSource();
In fact you can create a single MouseListener to add to each panel instead of creating a different listener for each panel because the above code is generic.
I am trying to make a thread that reads the screen and displays it in a frame, this code is meant to run at 5fps, so far it reads the screen, but I am having trouble making the JFrame display the updating Image each "frame" or 200 mili-seconds. when I use repaint(); or revalidate();
public static void startScreenRecorder()
{
Thread screenThread = new Thread()
{
public synchronized void run()
{
long time;
long lastFrameTime = 0;
JFrame frame = new JFrame("Screen capture");
ImagePanel panel = new ImagePanel(captureScreen());
frame.add(panel);
frame.setSize(300, 400);
frame.setVisible(true);
while (true)
{
time = System.currentTimeMillis();
while (time - lastFrameTime < 190)
{
try {
Thread.sleep(10);
} catch (Exception e) {
}
time = System.currentTimeMillis();
}
lastFrameTime = time;
panel = new ImagePanel(captureScreen());
panel.revalidate();
panel.repaint();
frame.revalidate();
frame.repaint();
}
}
};
screenThread.start();
}
Don't use Thread.sleep() to attempt to control animation.
Animation should be done by using a Swing Timer. When you use a Timer the GUI is automatically updated on the EDT.
panel = new ImagePanel(captureScreen());
The above code doesn't do anything. It just creates a panel in memory. Nowhere to you actually add the panel to the GUI. Changing the reference of a variable does not update the GUI.
Instead you should probably add a JLabel to the frame (when you initially create the frame). Then when you have a new Image you just do:
label.setIcon( new ImageIcon( your screen capture ) );
I wouldn't be surprised if your code shows no images at all since it ignores Swing threading rules:
All Swing code needs to be called on the Swing event dispatch thread (EDT) only.
All other long-running code needs to be called in a background thread. I assume that this means captureScreen().
You should never call Thread.sleep(...) on the Swing event thread unless you want to put your entire application to sleep.
Better perhaps to use a Swing Timer.
You create new ImagePanels but do nothing with them -- you never add them to the GUI for instance, except for the first JPanel. Note that if you change the object a variable refers to, here the panel variable, this will have absolutely no effect on instances of the object used elsewhere, there the JPanel displayed in the GUI.
Rather than create new JPanels, why not instead create ImageIcons with your images and swap a visualized JLabel's Icon with setIcon(...)?
Since you have a lot of background stuff going on, consider using a SwingWorker<Void, Icon> to do your work, and have it publish ImageIcons that are then displayed in the GUI's JLabel. If you did this, then you probably wouldn't use a Swing Timer since the timing would be done in the SwingWorker's background thread.
For example:
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
#SuppressWarnings("serial")
public class SwingWorkerEg extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = 400;
private JLabel displayedLabel = new JLabel();
public SwingWorkerEg() {
setLayout(new BorderLayout());
add(displayedLabel);
try {
MySwingWorker mySwingWorker = new MySwingWorker();
mySwingWorker.execute();
} catch (AWTException e) {
e.printStackTrace();
}
}
public void setLabelIcon(Icon icon) {
displayedLabel.setIcon(icon);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private class MySwingWorker extends SwingWorker<Void, Icon> {
private final Rectangle SCREEN_RECT = new Rectangle(0, 0, PREF_W,
PREF_H);
private Robot robot = null;
public MySwingWorker() throws AWTException {
robot = new Robot();
}
#Override
protected Void doInBackground() throws Exception {
Timer utilTimer = new Timer();
TimerTask task = new TimerTask() {
#Override
public void run() {
BufferedImage capturedImage = captureScreen();
publish(new ImageIcon(capturedImage));
}
};
long delay = 200;
utilTimer.scheduleAtFixedRate(task, delay, delay);
return null;
}
#Override
protected void process(List<Icon> chunks) {
for (Icon icon : chunks) {
setLabelIcon(icon);
}
}
private BufferedImage captureScreen() {
BufferedImage img = robot.createScreenCapture(SCREEN_RECT);
return img;
}
}
private static void createAndShowGui() {
SwingWorkerEg mainPanel = new SwingWorkerEg();
JFrame frame = new JFrame("SwingWorker Eg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Which would display...