I am messing around with some ideas for a side project and I would like to create a GUI using Java swing that doesn't look like it is from Windows95. One of the ideas I was kicking around was to use JLabels as buttons instead of the standard JButton. This would allow me to customize hover, drag, and movement effects as I like.
Research into the MouseAdapter class should allow me to do everything I intend, unfortunately I am having some trouble implementing the hover effect as I wanted as the JLabel does not appear to update. I have tried updating the Frame directly by calling frame.update(getGraphics()); but that does not appear to work as I think it does.
Can I get some advice on how to update the label properly.
Note: This is just an example with no effort put in to organize the code efficiently
public class Window extends JFrame {
/**
*
*/
private static final long serialVersionUID = 5259700796854880162L;
private JTextField textField;
private JLabel lblNewLabel;
static Window frame;
int i = 0;
public Window() {
JPanel panel = new JPanel();
getContentPane().add(panel, BorderLayout.CENTER);
panel.setLayout(null);
lblNewLabel = new JLabel("New label");
lblNewLabel.setBackground(Color.LIGHT_GRAY);
lblNewLabel.setBounds(137, 38, 114, 70);
panel.add(lblNewLabel);
lblNewLabel.addMouseListener(new LabelAdapter());
textField = new JTextField();
textField.setBounds(122, 119, 86, 20);
panel.add(textField);
textField.setColumns(10);
}
private class LabelAdapter extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent e) {
textField.setText(String.valueOf(i));
i++;
}
#Override
public void mouseEntered(MouseEvent e) {
lblNewLabel.setBackground(Color.CYAN);
}
#Override
public void mouseExited(MouseEvent e) {
lblNewLabel.setBackground(Color.LIGHT_GRAY);
}
}
/**
* #param args
*/
public static void main(String[] args) {
frame = new Window();
frame.setSize(900, 700);
frame.setVisible(true);
}
}
Window is reserver name for awt.Window, change this name to e.g. MyWindow
JPanel has implemented FlowLayout, you can't to use NullLayout use built_in LayoutManager, then to use JFrame.pack() before JFrame.setVisible for proper sizing on the screen
JLabel is transparent, change that by using JLabel.setOpaque(true);
refresh of Backgroung Color from Mouse over/hover isn't possible without JLabel.repaint() as last code line in concrete mouse_event, repaint() missing in JLabel API
On top of mKorbel's answer...
I don't know why you're going to so much effort, when you could actually make a button look like a label.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.ButtonModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class NotALabel {
public static void main(String[] args) {
new NotALabel();
}
public NotALabel() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
final JButton btn = new JButton("Am I label or a button?");
btn.setContentAreaFilled(false);
btn.setBorderPainted(false);
btn.setFocusPainted(false);
btn.setOpaque(true);
btn.getModel().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
ButtonModel model = (ButtonModel) e.getSource();
if (model.isRollover()) {
btn.setBackground(Color.CYAN);
} else {
btn.setBackground(null);
}
}
});
btn.addActionListener(new ActionListener() {
private int count = 0;
#Override
public void actionPerformed(ActionEvent e) {
count++;
((JButton) e.getSource()).setText("I'm a super button!! Or label...");
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.add(btn);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
You should also consider trying to Setting the look and feel or even possibly Modifying the look and feel
Related
I am trying to make a text adventure game that includes puzzles in every room. I have direction buttons to go to the next rooms, but I want the buttons to display a message if they haven't answered the question correctly yet. When they answer it then the game will show them which way they can go and the buttons will work. Is there anyway to do this?
I tried doing this, but the only way this works is if they type the answer and leave it in the textfield instead of typing enter. I don't want that though.
private void northBtnMouseClicked(java.awt.event.MouseEvent evt) {
if(!playerInput.getText().equals(currentRoom.getAns()))
{
gameScreen.append("\nThe doors are locked until you answer the puzzle.");
}
else
{
String direction = "north";
Room nextRoom = currentRoom.getExits(direction);
if(nextRoom == null)
{
gameScreen.append("\n There is not an exit that way\n");
}
else
{
currentRoom = nextRoom;
gameScreen.append("\n" + currentRoom.getLongDescription());
}
}
}
There a number of ways this might be achieved, one of the simplest might be to simply disable the button(s) until the user enters what you want.
The (minor) problem with this is that it will require the user to press the "action" key (typically Enter) until the input can be validated. Not a big deal, but its not always obvious to the user.
Another approach would be to use a DocumentListener to monitor for changes to the underlying Document and validate the input in real time.
This approach allows you to provide real time feedback to the user as they type. It also allows the user to paste content into the field and have it validated as well, which is not done if you use a KeyListener.
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public class TestDoc {
public static void main(String[] args) {
new TestDoc();
}
public TestDoc() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTextField field;
private JButton button;
public TestPane() {
button = new JButton("Make it so");
button.setEnabled(false);
field = new JTextField(10);
field.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
validateInput();
}
});
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(field, gbc);
add(button, gbc);
field.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void insertUpdate(DocumentEvent e) {
validateInput();
}
#Override
public void removeUpdate(DocumentEvent e) {
validateInput();
}
#Override
public void changedUpdate(DocumentEvent e) {
validateInput();
}
});
}
public void validateInput() {
String text = field.getText();
if ("go".equals(text)) {
button.setEnabled(true);
} else {
button.setEnabled(false);
}
}
}
}
Have a look at Listening for Changes on a Document for more details
I just give you this example to give you an idea and start you up
Code:
public class NewClass {
public static void main(String... args) {
JButton jb = new JButton("Enter");
jb.setEnabled(false);
JTextField jt = new JTextField(15);
JFrame jf = new JFrame();
jf.setSize(200, 100);
FlowLayout layout = new FlowLayout();
jf.setLayout(layout);
jf.add(jb);
jf.add(jt);
jf.setLocationRelativeTo(null);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
jt.addKeyListener((new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
JTextField textField = (JTextField) e.getSource();
String text = textField.getText();
textField.setText(text.toUpperCase());
if(!textField.getText().isEmpty())
jb.setEnabled(true);
else
jb.setEnabled(false);
}
}));
}
}
output:
Explanation:
There are many ways to accomplish what you want. For example, Key Listener and Document Listener.
The good practice is to use Document Listener because it is not depended on activities.
Note: there are other good ways to solve your issue but I thought this way is more easier for you to understand
Take a look at this comment
A DocumentListener would be better as that lets you react to any
changes to the text, whether or not they were triggered by a key press
(e.g. a paste from the clipboard), and you don't have to worry about
key presses that don't cause the text to change. – Ian Roberts
Source: http://docs.oracle.com/javase/tutorial/uiswing/events/keylistener.html
Using Document Listener Approach which is good practice and recommended
Code:
import java.awt.FlowLayout;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.Document;
public class DocumentListenerSample {
public static void main(String args[]) {
JButton jb = new JButton("Enter");
jb.setEnabled(false);
JTextField jt = new JTextField(15);
JFrame jf = new JFrame();
jf.setSize(200, 100);
FlowLayout layout = new FlowLayout();
jf.setLayout(layout);
jf.add(jb);
jf.add(jt);
jf.setLocationRelativeTo(null);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
DocumentListener documentListener = new DocumentListener() {
#Override
public void changedUpdate(DocumentEvent documentEvent) {
printIt(documentEvent);
}
#Override
public void insertUpdate(DocumentEvent documentEvent) {
printIt(documentEvent);
}
#Override
public void removeUpdate(DocumentEvent documentEvent) {
printIt(documentEvent);
}
private void printIt(DocumentEvent documentEvent) {
Document source = documentEvent.getDocument();
int length = source.getLength();
if(length != 0)
jb.setEnabled(true);
else
jb.setEnabled(false);
}
};
jt.getDocument().addDocumentListener(documentListener);
jf.setSize(250, 150);
jf.setVisible(true);
}
}
Sure, you could either jbtn.setEnabled(false) until you had all your answers or (probably easier) keep an answer count accessible to your MouseClicked routine that simply ignores clicks until the questions are answered.
I have switched my Layout to a AbsoluteLayout and everything is in place where it needs to be, but when I run my program, my JButtons are invisible until I mouse over them. What should I change so they are always visible, mouse entered or not?
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import java.awt.Color;
import java.awt.Cursor;
public class Library extends JFrame implements ActionListener, MouseListener {
private JFrame jf1;
private JPanel jp1;
private JTextField jtf1;
private JTextField jtf2;
private JTextField jtf3;
private JButton jb1;
private JButton jb2;
private JButton jb3;
public Library() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch(Exception ee) {
ee.printStackTrace();
}
JFrame.setDefaultLookAndFeelDecorated(false);
jf1 = new JFrame("Library");
jf1.setVisible(true);
jf1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf1.setSize(1080, 900);
jf1.setResizable(true);
jf1.getContentPane().setLayout(null);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
jf1.setLocation(dim.width/2-jf1.getSize().width/2, dim.height/2-jf1.getSize().height/2);
jp1 = (JPanel) jf1.getContentPane();
jb1 = new JButton("Genre");
jb1.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
jb1.setBounds(345, 11, 150, 60);
jb1.addActionListener(this);
jb2 = new JButton("Author");
jb2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
jb2.setBounds(494, 11, 150, 60);
jb2.addActionListener(this);
jb3 = new JButton("Title");
jb3.setDefaultCapable(false);
jb3.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
jb3.setBounds(643, 11, 150, 60);
jb3.addActionListener(this);
jtf2 = new JTextField("Enter Text");
jtf2.setBounds(445, 292, 200, 20);
jtf2.addMouseListener(this);
jtf2.setVisible(false);
jtf3 = new JTextField("Enter Text");
jtf3.setBounds(671, 351, 200, 20);
jtf3.addMouseListener(this);
jtf3.setVisible(false);
jp1.add(jtf2);
jtf1 = new JTextField("Enter Text");
jtf1.setBounds(236, 230, 200, 20);
jtf1.addMouseListener(this);
jtf1.setVisible(false);
jp1.add(jtf1);
jp1.add(jtf3);
jp1.add(jb1);
jp1.add(jb2);
jp1.add(jb3);
jf1.validate();
}
#Override
public void actionPerformed(ActionEvent e) {
Object code = e.getSource();
if (code == jb1) {
jtf1.setVisible(true);
jp1.validate();
}
else if (code == jb2) {
jtf2.setVisible(true);
jp1.validate();
}
else if (code == jb3) {
jtf3.setVisible(true);
jp1.validate();
}
}
public static void main(String[] args) {
Library shoe = new Library();
}
#Override
public void mouseClicked(MouseEvent eee) {
Object mouseCode = eee.getSource();
if(mouseCode == jtf1) {
jtf1.selectAll();
}
if(mouseCode == jtf2) {
jtf2.selectAll();
}
if(mouseCode == jtf3) {
jtf3.selectAll();
}
}
#Override
public void mouseEntered(MouseEvent eee) {}
#Override
public void mouseExited(MouseEvent eee) {}
#Override
public void mousePressed(MouseEvent eee) {}
#Override
public void mouseReleased(MouseEvent eee) {}
}
There are multiple errors in your program:
You're using AbsoluteLayout which in the end it's still a null layout, see Null layout is evil and Why is it frowned upon to use a null layout in Swing?. While absolute positioning might seem like the fastest and easiest way to make complex GUI's for Swing beginners, the more you advance in your program the more errors you'll get due to this. Instead use a Layout Manager or combinations of them along with Empty borders for extra spacing if needed.
You're creating a JFrame object and extending JFrame that, in other words, makes your class a JFrame! There's no need to extend JFrame if you really need to extend something extend a JPanel which can be added later into other components while JFrame can't.
You're making your JFrame visible before you have added all your components to it, that's the reason for your error, and probably the 1st one too. You should make it visible only in the end, after you have added all your components to it.
Set the JFrame visible (jf1.setVisible(true);) after you add all components. (End of your constructor)
I am trying to create a JTextArea which scrolls to bottom every time a text is appended to that text area. Otherwise, the user should be able to scroll top and see previous message. I used this code:
JTextArea terminalText = new JTextArea();
JPanel terminal = new JPanel();
terminal.setLayout(new BorderLayout());
add(terminal); //Adds the terminal to mother JPanel
//I added scrollbar to my JTextArea
JScrollPane scroll = new JScrollPane(terminalText);
terminal.add(scroll, BorderLayout.CENTER);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scroll.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
e.getAdjustable().setValue(e.getAdjustable().getMaximum());
}});
So far this code seems to make my text area scroll to bottom of the terminalText text area every time I append something to terminalText using terminalText.append.
However, the user cannot use scroll bar to scroll to the top to see previous message. Is there a way to fix this? Should I be using DocumentListener to achieve this?
Check out Smart Scrolling.
If the scrollbar is at the bottom, then as text is appended you will see the new text.
If the user has scrolled to a different position, then the viewport will stay there until the user scrolls back to the bottom.
As a simple (and rough) proof of concept...
This basically adds a DocumentListener to the JTextArea and on any Document event, use setCaretPosition to move the caret to the end of the document.
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
import java.util.WeakHashMap;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.JTextComponent;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JTextArea ta = new JTextArea(10, 20);
ta.setWrapStyleWord(true);
ta.setLineWrap(true);
MoveToTheBottom.install(ta);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(ta));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
ta.append(new Date().toString() + "\n");
}
});
timer.start();
}
});
}
public static class MoveToTheBottom implements DocumentListener {
private static WeakHashMap<JTextComponent, DocumentListener> registry = new WeakHashMap<>(25);
private JTextComponent parent;
protected MoveToTheBottom(JTextComponent parent) {
this.parent = parent;
parent.getDocument().addDocumentListener(this);
}
public static void install(JTextComponent parent) {
MoveToTheBottom bottom = new MoveToTheBottom(parent);
registry.put(parent, bottom);
}
public static void uninstall(JTextComponent parent) {
DocumentListener listener = registry.remove(parent);
if (listener != null) {
parent.getDocument().removeDocumentListener(listener);
}
}
#Override
public void insertUpdate(DocumentEvent e) {
parent.setCaretPosition(e.getDocument().getLength());
}
#Override
public void removeUpdate(DocumentEvent e) {
parent.setCaretPosition(e.getDocument().getLength());
}
#Override
public void changedUpdate(DocumentEvent e) {
parent.setCaretPosition(e.getDocument().getLength());
}
}
}
The example demonstrates a possible re-usable API which you can use to "install" and "uninstall" the support as reqiured
I wanted to create a fairly simple GUI program that switches between panels depending on buttons the user clicked. I searched around and came up with CardLayout being the best suggestion.
Basically in the examples of CardLayout, you create a "card" (a JPanel) and then add each component, like buttons, etc... and switch between the cards.
What I want to create is an object that is a "card" with all the components set up already, in a separate class, and just create an instance of that in the main program. I am a beginner and do not know the best design practices, so I didn't want to create my own class that extended JPanel, which I am pretty sure is terrible design.
You do it just like you would if you had create an instance of JPanel and add the components directly to it.
You need to ensure that the custom class extends from something JComponent or JPanel (preferably) and add them to the container like any other component, for example...
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestCard {
public static void main(String[] args) {
new TestCard();
}
public TestCard() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final CardLayout cardLayout = new CardLayout();
final JPanel cardPane = new JPanel(cardLayout);
cardPane.add(new Card01(), "Card01");
cardPane.add(new Card02(), "Card02");
JToggleButton btnCard01 = new JToggleButton("#1");
JToggleButton btnCard02 = new JToggleButton("#2");
ButtonGroup bg = new ButtonGroup();
bg.add(btnCard01);
bg.add(btnCard02);
JPanel buttons = new JPanel();
buttons.add(btnCard01);
buttons.add(btnCard02);
btnCard01.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
cardLayout.show(cardPane, "Card01");
}
});
btnCard02.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
cardLayout.show(cardPane, "Card02");
}
});
btnCard01.setSelected(true);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(cardPane);
frame.add(buttons, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Card01 extends JPanel {
public Card01() {
setLayout(new GridBagLayout());
add(new JLabel("#1"));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public class Card02 extends JPanel {
public Card02() {
setLayout(new GridBagLayout());
add(new JLabel("#2"));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
I want to show a changing image on my frame. The imagepath is always the same, but the image will be getting overwritten every 10 seconds from another program.
The problem is that the image is not changing when I overwrite it with another image with the same name. So in my understanding: Compiler looks every look in the path and gets the image -> when the image changed it will be changed on the frame!
I hope you understand my problem and somebody could help me.
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.io.File;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class GUI extends JFrame{
public ImageIcon imageBar;
public JLabel labelimage1;
private JLabel labelimage2;
private JLabel bar1 = new JLabel();
private JLabel bar2 = new JLabel();
private JLabel bar3 = new JLabel();
private JLabel bar4 = new JLabel();
private JLabel bar5 = new JLabel();
private JButton buttonBar1 = new JButton("1");
private JButton buttonBar2 = new JButton("2");
private JButton buttonBar3 = new JButton("3");
private JButton buttonBar4 = new JButton("4");
private JButton buttonBar5 = new JButton("5");
private JPanel panel1 = new JPanel();
private JPanel panel2 = new JPanel();
private JPanel panel3 = new JPanel();
private JFrame window = new JFrame("Interface");
public GUI(){
//set the layouts
panel1.setLayout(new GridLayout(1, 2));
panel2.setLayout(new GridLayout(2, 1));
panel3.setLayout(new GridLayout(2, 5));
//place Panel2 and Panel3 in the window
panel1.add(panel2);
panel1.add(panel3);
//----Panel2
//refreshImage();
//----Panel3
panel3.add(buttonBar1); //add the bars 1-5 on panel3
panel3.add(buttonBar2);
panel3.add(buttonBar3);
panel3.add(buttonBar4);
panel3.add(buttonBar5);
//configure the frame
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
window.setSize(800, 400);
window.getContentPane().add(panel1);
}
public void refreshImage() {
panel2.removeAll(); //delete the old panel
//panel2.repaint();
//panel2.revalidate()
DrawImage pan = new DrawImage();
panel2.add(pan);
panel2.add(labelimage2);
}
}
import javax.swing.ImageIcon;
import javax.swing.JPanel;
public class DrawImage extends JPanel implements ActionListener{
private ImageIcon image;
public DrawImage(){
image = new ImageIcon("C:\\Users\\usuario\\Desktop\\image.png");
}
protected void paintComponent(Graphics g){
super.paintComponent(g);
image.paintIcon(this, g, 50, 50);
repaint();
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
}
import java.io.File;
public class Main {
public static void main(String[] args) {
GUI Interface = new GUI();
while(true)
{
Interface.refreshImage();
try {
Thread.sleep(5000); //wait for 5000ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Thank you very much!
The likely cause is Java is caching the image in memory, associated with the source name. So rather then trying to reload the image again, Java simply returns the cached version.
You could use ImageIcon#getImage#flush to force Java to reconstruct the image
Problems
You are calling refreshImage from a Thread other then the Event Dispatching Thread, this could cause issues with the updating of the components and cause rendering artifacts
You are forcefully removing the DrawImage pane and adding a new instance, rather the trying to reload the image
You're calling repaint within the paintComponent method, don't do this...
You should consider using a Swing Timer, which will allow you to schedule a regular update and be notified within the context of the Event Dispatching Thread.
You could provide a simple refresh method which flushes the current ImageIcon and schedule a repaint of the panel...or you could just use a JLabel and save your self the time
An example of Image#flush
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SlideShow {
public ImageIcon imageBar;
public static void main(String[] args) {
new SlideShow();
}
public SlideShow() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DrawImage());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DrawImage extends JPanel {
private ImageIcon image;
public DrawImage() {
image = new ImageIcon("D:\\thumbs\\image.png");
Timer timer = new Timer(5000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
refresh();
}
});
timer.start();
}
public void refresh() {
image.getImage().flush();
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image.getImage(), 0, 0, this);
}
}
}
The problem with this, is because the image data is loaded in a background thread, it won't may no be available when the component is first repainted, which could make the component appear to flicker.
A better approach would be to use ImageIO.read, which will ensure that the image is fully loaded before the method returns, the draw back here is that could cause the application to "pause" momentary as the image is loaded, personally, I'd use the refresh method to stop the the Timer (or set the Timer to non-repeating), start a background Thread to load the image (using ImageIO.read) call repaint (which is thread safe) and restart the Timer...
Your while (true) loop risks typing up the Swing event thread locking your program. If it doesn't do that, then you risk unpredictable threading issues by making Swing calls off of the event Thread. These problems can be solved easily by your using a Swing Timer not a while true loop to do your swapping.
Rather than removing and adding components, why not simply display images as ImageIcons within a single non-swapped JLabel.
To swap images here, simply call setIcon(...) on the JLabel.
For an example of using a Swing Timer to swap images, please check out my answer to a similar question here.
For example:
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
public class TimerImageSwapper {
public static final String[] IMAGE_URLS = {
"http://imaging.nikon.com/lineup/dslr/d7000/img/sample/img_01.png",
"http://imaging.nikon.com/lineup/dslr/d7000/img/sample/img_02.png",
"http://imaging.nikon.com/lineup/dslr/d7000/img/sample/img_04.png",
"http://imaging.nikon.com/lineup/dslr/d3200/img/sample/img_08.png",
"http://imaging.nikon.com/lineup/dslr/d3200/img/sample/img_05.png",
"http://imaging.nikon.com/lineup/dslr/d3200/img/sample/img_01.png",
"http://imaging.nikon.com/lineup/dslr/d3200/img/sample/img_06.png" };
private ImageIcon[] icons = new ImageIcon[IMAGE_URLS.length];
private JLabel mainLabel = new JLabel();
private int iconIndex = 0;;
public TimerImageSwapper(int timerDelay) throws IOException {
for (int i = 0; i < icons.length; i++) {
URL imgUrl = new URL(IMAGE_URLS[i]);
BufferedImage image = ImageIO.read(imgUrl);
icons[i] = new ImageIcon(image);
}
mainLabel.setIcon(icons[iconIndex]);
new Timer(timerDelay, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
iconIndex++;
iconIndex %= IMAGE_URLS.length;
mainLabel.setIcon(icons[iconIndex]);
}
}).start();
}
public Component getMainComponent() {
return mainLabel;
}
private static void createAndShowGui() {
TimerImageSwapper timerImageSwapper;
try {
timerImageSwapper = new TimerImageSwapper(5 * 1000);
JFrame frame = new JFrame("Timer Image Swapper");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(timerImageSwapper.getMainComponent());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}