java event for when the component becomes visible - java

I have a JScrollPane wrapped around a JPanel that contains potentially hundreds of JLabels that show thumbnail images (one thumbnail per JLabel). For memory reasons I don't want to build all the thumbnails. I want to build the thumbnails only for the JLabels that are visible and remove the thumbnails when their JLabels become not visible. They become visible/invisible when the user scrolls the JPanel. I tried to implement the loading/unloading the thumbnail by using the ComponentListener like this:
addComponentListener( new ComponentAdapter() {
#Override
public void componentShown( ComponentEvent e ) {
setIcon( new ImageIcon( getThumb() ) );
}
#Override
public void componentHidden( ComponentEvent e ) {
setIcon( null );
}
});
But this doesn't work. The JLabels are always empty. I could use the scroll event and calculate which thumbnails should be loaded but before I do that I would like to know if there is a simpler solution.

The "visible" property does not mean visible "on screen". It only indicates if the the component itself is to be displayed or not. Since components are visible by default and the listeners are only notified when a property changes your listener is never notified.
To the best of my knowledge there is no dedicated event involved telling a component when it enters the visible region of the display. Also note that setting an icon on a label may alter its preferred size, breaking the entire layout. This can be worked around by manually giving the labels a fixed preferred size (which should be simple in case of thumbnails).
A lazy approach would be to overwrite paintComponent on the labels and check if the thumb needs to be loaded in paintComponent:
protected void paintComponent(Graphics g) {
if (getIcon() == null) {
// create thumbnail
}
super.paintComponent(g);
}
This isn't the best approach, as your code will run inside Swings event dispatch thread. This means any delay in loading the thumbnail will block rendering of your UI.
A saner approach IMO would be to just request loading of the thumbnail and defer the actual loading to a background thread. When that thread completes loading, it can then use SwingUtilities.invoke (or invokeLater) to update the label (which triggers a repaint automatically if I'm not mistaken).
The effect would be that the labels scrolled in briefly show empty, then update as soon as the thumb is available.

Related

JPanel removeAll doesn't get rid of previous components

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.

Get notified when the user finishes resizing a JFrame

I have a fractal generation component (a subclass of JPanel) inside a JFrame. When the user resizes the window, it takes quite a while to update the fractal to the new size.
I currently have a ComponentListener on the JPanel, but its componentResized event is called every time the user moves the mouse while dragging the window border. This means that the fractal is told to resize many times, and slowly (over the course of a few minutes) grows to the new size.
Is there a way to be notified when the user releases the mouse button, so that I can only change the fractal size when the user has finished resizing?
Others have reported this happening when the listener is attached to the JFrame instead, but this doesn't work for me (and others), for some reason.
Instead of starting the calculation each time you receive a receive-event, you can only start the calculation after you received the last event by using a timer, e.g. in pseudo-code (or at least code which I typed here directly and not in my IDE)
private Timer recalculateTimer = new Timer( 20, myRecalculateActionListener );
constructor(){
recalculateTimer.setRepeats( false );
}
#Override
public void componentResized(ComponentEvent e){
if ( recalculateTimer.isRunning() ){
recalculateTimer.restart();
} else {
recalculateTimer.start();
}
}
And you can still combine this with Andrews suggestion to use an image which you stretch until the calculation has actually finished.
you have look at HierarchyListener, where you can listening for HierarchyEvent with interface to HierarchyBoundsListener
basically nothing wrong with ComponentListener, but you have to wrapping expected events to the Swing Timer, in the case that events repeated, only to call Timer#restart(), output from Swing Timer should be Swing Action
It's a bit late, but it looks like no one found the correct answer. I found that when you call child.updateUI() inside a ComponentListener (componentResized block) of a window, this child resizes it self and updates its content. Using timers is unsafe.

Java add image from an event

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().

Clickable images in Java

I'm making a point-and-click escape type of game. I'm wondering, if there's an easy way to make clickable images? I'm going to use photographs as background and also as items that the player has to collect. So, is there an easy way to make the items clickable and also disappear after clicked (player collects it).
Thanks for answers, and if my explanation was complicated, please say and I'll try to fix it.
For making a game I'd recommend bringing all the logic one level lower.
Create an data structure which will contain the state of your game "level". This data structure will be loaded from some kind of XML level configuration file, and I think it should contain:
A Image object containing the level background (photo).
An array of all items. Each item should have an Image, dimensions on the level screen (X,Y,Width,Height) and some kind of state (visible, highlighted, etc.).
Make a class which extends Canvas. This will be the component which will contain and render your whole game screen (with items, and background photo).
Override it's paint method. In this paint method use drawImage method go through your level data object (specified in step 1) and draw the background (room) and all the items in their respective coordinates. If the item has visible = false - don't draw it. If it has selected = true - draw some highlight around it or whatever you want.
Implement a MouseListener. This listener should check if click coordinates are inside the dimensions of one of your "objects" on the screen (loop through all clickable objects). If it is - do appropriate action (for example increase score and set visible = false for that item) and update your canvas with repaint. This will trigger the paint method again drawing all the changes on your canvas.
Register a MouseListener on your Canvas with addMouseListener to tie it all together.
If you're using Swing, simply set the icon of a JButton. This will create a "clickable image".
There are several ways, the most straightforward being using a JButton and setting an icon on it. But you can also add a MouseListener to any Component (like JPanel) and set an image as background (override paint).
You're going to probably do something like this: Draw a JPanel and then position a bunch of JLabels on it, and each label will draw it's own image too.
If you have a specific code question, we can be more help, but you're being very general. Try working through the Swing examples on the java web site and then ask more targeted questions.
You can set the image to a JButton object as background. The key point is that you should listen to the mouse click event and JButton is the first choice to satisfy this requirement.
A simple example on how you can get a clickable image. For more examples and explanations on java Swing and awt you can look at the official java tutorials here.
//a lable holding an image
JLabel label = new JLabel(new ImageIcon("MyImage));
//Add a mouse listener to get the click event
//
label.addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent e) {
System.out.println("Mouse clicked (# of clicks: "
+ e.getClickCount() + ")", e);
}
});

JAVA: How to use paint() method to update the previously drawn object?

How to use paint() such that when repaint() is called the previously drawn object should not get deleted when drawing the new object. That is the drawing area must get UPDATED only and not REDRAWN.
In my code when one of many button is clicked, some aplhabet get displayed. I want to have functionality such that when other buttons are clicked the previously drawn alhabets must be present.Take is as if a string of alphabets getting created as the buttons are clicked.
Im using Java Swing for coding.
Some piece of my code:
if(source == btnAlpha[i] )
bollyDraw.repaint(); //bollydraw is an object of a JPanel extended class having implemented the paintComponent(Graphics g) method
In the paint() method:
if (word[i] == key) {
g.drawChars(word, i, 1, x, y);
}
In a project I worked on I used a List to store the objects that were to be drawn as a member of a class. Then as the user interacted with my UI I added/removed items from this list. The JPanel that rendered the items painted the items that were in this list. It's helps separate the UI logic from the paint logic as you can determine what goes into the paint list when an event is fired rather than in the paint method ( which should be as clean as possible ). However this will force you to repaint everything on every paint call.
In conjunction with this Kim's RepaintManager is a great way to limit what gets repainted. It is region based so there is some complexity in determining what region of the screen has changed. If you have the time it is better to use something like this otherwise it could be difficult to add this functionality down the road.
Your app must be prepared to re-paint anything it has painted. From your description, I'm afraid that means you have to keep track of everything you've painted.
The Swing behavior is partially dictated by the underlying window manager and graphical system, which at any time may chose to paint over an area where your application is present. You can override update() to control repaints initiated by your own app, and might be able to improve performance by implementing your own RepaintManager.
This tutorial explains Swing painting in more detail: http://java.sun.com/products/jfc/tsc/articles/painting/
Custom Painting Approaches shows a couple of ways to do this depending on your exact requirement.

Categories