I have a swing application in which I display images in a JPanel. If the app is unable to produce the image I want to remove the previous one from the JPanel and replace it with a JTextField and message. I can add the text field , but it's drawn on top of the previous contents, which is itself a subclass of JPanel. Here's what I have:
private void displayMessage(String message) {
JTextField tf = new JTextField(message);
cdPanel.removeAll();
cdPanel.add(tf, BorderLayout.NORTH);//tried lots of variations, inc. no layout
cdPanel.validate();
}
How can I get cdPanel to completely redraw itself?
You can simply try calling :
cdPanel.revalidate();
cdPanel.repaint(); // This is required in some cases
instead of
cdPanel.validate();
As you are dealing with unpredictable latency, use a SwingWorker to do the loading in the background, as shown here. The example uses pack() to resize the label to that of the image, but you may want to use a fixed-size grid and scale the images, as shown here.
Related
I am setting a JLabel for the error messages in my program, so initially the label is empty label.setText(""), but when there is an error it should change to something like label.setText("Error, you have entered invalid data...").
If I use setSize(x,y) on the label, it forces other components to displace when error message takes place. But using setPreferredSize(Dimension(x,y))doesn't impact them.
Q1. Why is that?
Q2. What is the difference between setSize(x,y) and setPreferredSize(Dimension(x,y))
Q3. Does it have to do anything with layout?
Thank you in advance for explanation!
P.S. I am using GridBagLayout for positioning my components on the JPanel.
Don’t use the setSize method.
setSize is called by LayoutManagers, like GridBagLayout, to lay out child components. When you call setSize explicitly, you are fighting with the GridBagLayout. Eventually, GridBagLayout will undo your setSize call, when it calls setSize for its own purposes.
In other words, any call to setSize eventually will be wiped out by the parent layout.
setPreferredSize will not be wiped out. Most LayoutManagers, including GridBagLayout, do their best to respect a component’s preferred size.
However, you should not be calling setPreferredSize. Components already have a preferred size by default, and it is almost certainly better than any numbers you can come up with. For instance, a JLabel’s default preferred size is the size which is just large enough to accommodate its text, icon, and borders.
Computing a preferred size is harder than you might think. How many pixels does text use? How many pixels high is a 12 point font? 12 points is not 12 pixels. 12 points is 12⁄72 inch. How many pixels is that? It depends on the user’s monitor and graphics resolution. All of this is known to the Swing rendering system, and JLabel uses all of that information to determine its default preferred size. You should not try to reinvent all of that work, and you should not try to replace that work with something simpler, as it will be inadequate.
If you just let the JLabel keep its preferred size, GridBagLayout will do its best to accommodate that. If the window itself does not have room to display the JLabel’s new text, you probably should call the window’s pack() method after changing the text.
Update: This appears to be an XY problem—you really want a message that you can show and hide.
You want your layout to be big enough to accommodate your message text as soon as you create it. This is typically done with a CardLayout, which lets you place several components on top of each other, with only one of them visible at any given moment. Since you want to show no text at all, initially, you would add an empty JLabel as the first component in the CardLayout, so it is shown by default:
JLabel label = new JLabel("Error, you have entered invalid data...");
CardLayout messageLayout = new CardLayout();
JPanel messagePane = new JPanel(messageLayout);
messagePane.add(new JLabel(), "blank");
messagePane.add(label, "message");
// Do not add label directly to your user interface.
// Add messagePane instead.
mainWindow.add(messagePane);
// ...
// Show message
messageLayout.show(messagePane, "message");
// ...
// Hide message
messageLayout.show(messagePane, "blank");
"message" and "blank" are never seen by the user. They are just unique identifiers for each component (“card”) in the CardLayout. You can make them anything you want.
The setSize() function sets the size not based on any LayoutManager. Thats why you should always use setPrefferedSize() when working with a LayoutManager. setPrefferedSize() firstly tries to be conform with the LayoutManagers dimensions if then possible Java tries to set the size of the Label according to your setPrefferedSize() input.
So yes, it does have anything to do with layout. If possible, you should only use setPrefferedSize() as you are working with layout managers.
So, I want to draw an image based on the current selection of a scroll list in java Swing. It seems the best way to do this is to add an label to a panel. I tried multiple various ways of doing this and for the life of me I can't figure why it won't display the image. This is a snippet of what I have managed to do so far.
private void jList1MouseClicked(java.awt.event.MouseEvent evt) {
ImageIcon greenDragon = new ImageIcon("C:\\Users\\Ilmari\\Documents\\NetBeansProjects\\GUI harkkatyö\\src\\Ile\\Green_dragon.png");
JLabel dragon = new JLabel();
dragon.setIcon(greenDragon);
String selectedMonster = jList1.getSelectedValue();
if(selectedMonster.equals("Green Dragon")){
jPanel1.add(dragon);
}
else if(selectedMonster.equals("Black Demon")){
}
}
The best outcome so far has been overriding the background JLabel image completely and only displaying a white box with the image.
JLabel dragon = new JLabel();
This label should be declared as an attribute of the class, and added to the GUI when it is first made. Then in the jList1MouseClicked method, simply call dragon.setIcon(..).
That way there is no need to revalidate the GUI on each image change.
On the subject of jList1MouseClicked: Use the most optimized listener for a JList. A ListSelectionListener will react to keyboard input as well as mouse input, and provides other advantages besides.
If the image isn't displayed at all. You need to revalidate and repaint your frame.
To achieve that - add this to your code:
frame.getContentPane().validate();
frame.getContentPane().repaint();
Is there any property or any code that I can implement to make an icon inside a JLabel resizable. Or is there any other item which can store an image, that I could use to have a resizable image inside a JFrame?
use this code :
photo = new ImageIcon(UrlofPhoto);
Image im = photo.getImage();
Image Newim = im.getScaledInstance(yourlabel.getWidth(), yourlabel.getHeight(),Image.SCALE_SMOOTH);
ImageIcon Newphoto=new ImageIcon(Newim);
yourlabel.setIcon(Newphoto);
don't forget to set an initialised size to yourlabel
Generally, I would't suggest trying this with a JLabel as JLabel has a lot of other features you really don't want to messing with (alignment and text).
Generally, a better solution is to use a dedicated "image" panel, which you can provide additional control over to fine tune how you want the scaling to work.
For example
If you're really stuck on using JLabel, I would recommend attaching a ComponentListener to it and resizing the underlying image when ever it changes size. The problem with this is componentResized may be called repeatedly in quick succession, meaning you will need to devise some kind of coalescing algorithm that only reacts to the last event within a given period of time...
Check out Darryl's Stretch Icon which will dynamically scale the icon to fill the label.
So I'm making this program with a GUI and I haven't worked with Swing/SWT too much but a little bit to know what's going on.
Anyway, I add an actionlistener for a button so it'll add an image to the contentPane when I click on the button but it doesn't work unless I have it as a JComponent (as seen below) and add my other things (button, JLabel, etc) to it afterwards...AND set this JComponent to the content view (which doesn't make sense).... I've also tried making it extend JPanel and just clearing out original contents and re-adding them to the new JPanel. The thing is, when I do this it recreates the text for my JLabel in a weird way, and I just know there's gotta be a simpler, more efficient, way.
class ShowImage extends JComponent{
public ShowImage(){
super();
monkey = Toolkit.getDefaultToolkit().getImage(("D:/monkey.png"));
}
public void paintComponent(Graphics g){
g.drawImage(monkey, 20, 100, null);
repaint();
}
}
Do not invoke repaint inside paintComponent
Invoke super.paintComponent and then draw the image
Also, depending on the layout manager, this component will have a preferred size of (0, 0), and therefore will not be visible.
For more information, see 2D Graphics.
Edit -
Note that dynamically adding a component will force you to revalidate the container and issue a repaint request so the layout manager will layout its components again and remove any visual artifacts. Also, for more information regarding images, see Working with Images.
Anyway, the simplest approach would probably be to set the image as the icon of a JLabel instance and add that to the container. There's really no need to reinvent the wheel here.
g.drawImage(monkey, 20, 100, this);
..would most likely have fixed the problem in the original code. It was a combined problem of:
Loading the image in an asynchronous way. (Toolkit.getImage() as opposed to ImageIO.read().)
Painting it to a 'blinkered' ImageObserver. The JComponent implements ImageObserver. As soon as the image is totally loaded (as well as a few points before that), the observer will be informed, triggering a repaint().
I need to display different drawings on a JPanel.
I have put the drawing files into an array, but when I changed it using a button, the JPanel only displays first drawing and doesn't change to the next drawing...
I have called panel.revalidate(), but it doesnt work.
This is the segment of the code that I used but not working.
The JPanel display was static.
String[] a = {"image1.txt","image2.txt","image3.txt"};
List<String> files = Arrays.asList(a);
public void actionPerformed(ActionEvent e) {
if (e.getSource() == answer1){
fileNumber++;
//call other class for painting (files=array files, fileNumber=index of the array)
draw = new drawingPanel(files,fileNumber);
panel.add(draw);
}
panel.revalidate();
panel.repaint();
}
You might try keeping a reference to your drawingPanel and calling remove() on the existing drawingPanel before re-adding it. According to the JPanel JavaDoc, the layout is FlowLayout by default - which will not replace the image like you are intending, but will instead place the next drawingPanel to the right of the previous one. (what happens when you resize the window?)
By the way, how do you handle the case where you get past the last image in the array?
Are you only displaying one drawing at a time? If so, you may want to try using a CardLayout, so you can switch between drawings easily. See http://java.sun.com/docs/books/tutorial/uiswing/layout/card.html for an example.
I had a similar issue the other day attempting to dynamically display different buttons on my UI depending which tab of a JTabbedPane the user picked. CardLayout was just the thing to make things easy.