Problem with mouseListener on JPanel - java

I have problem with mouseListener in JPanel.
I add JLabel which contain image(size 600 x 600) on JPanel, and I add mouseListener for the JPanel.
Everytimes I click on the image, the image should change to another image, and this is working fine. However, the problem is that, only if I click on the right side or at the center of the image, then the image will change to another image. The image doesn't change when I click on the top or on the left side. This make me confused. I want the image to change to another image when I click everywhere within the image display.
private final int SECOND= 1;
private final int FIRST = 0;
int imageCounter = 0;
JLabel picture;
JPanel panel;
...
private mainLayout () {
GridBagLayout m = new GridBagLayout();
Container c = (Container)getContentPane();
GridBagConstraints con;
c.setLayout (m);
picture = new JLabel();
picture.setIcon(getImages(myImage.get(imageCounter).get(FIRST))); //first Image
panel = new JPanel();
con = new GridBagConstraints();
con.anchor=GridBagConstraints.CENTER;
con.gridy = 1; con.gridx = 0;
m.setConstraints(panel, con);
c.add(panel);
panel.add(picture); //add the pictures
panel.addMouseListener(l);
....
}
MouseListener l = new MouseAdapter(){
public void mouseClicked (MouseEvent e)
{
Point p = e.getPoint();
if((panel.getBounds().contains(p))
picture.setIcon(getImages(myImage.get(imageCounter).get(SECOND)));
}
};

The problem looks as if it might be in this unnecessary code:
Point p = e.getPoint();
if((panel.getBounds().contains(p))
The mouse listener is on the panel, so the mouse coordinates will be relative to the panel top left. panel.getBounds() gets the bounds of the panel relative to whatever its parent container is.
It's worth noting the mouse event behave very strangely. They "bubble up" until they hit a component with a mouse listener attached. So, adding a mouse listener actually changes the behaviour of a component. Adding the listener to a parent will potentially miss events depending upon the exact way the component is set up (which may change arbitrarily). There are a number of ways around this, none of them good.

The likely problem you have is the same I have encountered, that your event is getting gobbled up in your hierarchy as a previous poster implied. In reference to camickr's reply, it really doesn't matter if you extend JPanel or JLabel as long as you're aware of the consequences. JPanel offers you a lot more capability and has many useful things already built into it. But let's take the simple example where you have the following:
A JFrame that contains a JPanel, call it's JParent. JParent has a set of mouse listeners dedicated to it.
JParent contains a child JPanel that's added to its components, called JChild. JChild also has its own set of mouse listeners.
What happened when you click on an area that is not occupied by JChild? You get the response of JParent's listeners. If you click in an area with JChild visible, only JChild's mouse events will fire.
So be very careful in debugging as Java's swing may have different behavior than you may be used to from other implementations of drawing simple forms/GUIs. Events are basically one-shot and the lowest level component gets first dibs. If you try to fire the parent's events anyway, your x/y coords will be based on the child and not the parent. I'm still trying to come up with a good work around myself because I need access specifically to the parent to move an object elsewhere.
The best I can think of that isn't a mess to debug/think about are these two ideas:
a) have each of your individual components contain their own exclusive set of events and try to prevent the 'bubbling up' effect from coming into play.
b) handle all events on the parent
I'd prefer a as it avoids nasty things such as checking component classes just to figure out what kind of reaction you want (i.e. things like what kind of right-click menu to show).
Edit: So you can handle this either way I've discovered. You can use the dispatchEvent message to forward the events back upward the chain. In my case I have to gobble up the first event (or alter it) and send the x-y positions as if they are relative to the parent (using offsets). I'd say the better way to do it is through forwarding because then all your individual components can be encapsulated.

Are you positive that you are actually clicking? The mouse click event only fires if the mouse is pressed and released in the exact same spot. If your mouse is moving even minimally while you are clicking, the event won't be fired.

Out of curiosity, why are you using a JLabel to display an image? You may be better off writing an ImagePanel class that extends JPanel and overrides the paint() method to do your own drawing. Then you can attach MouseListeners right to the panel.

Related

Can you make the background of a JScrollBar transparent?

I have a series of column labels that scrolls independently from the data that is displayed in a matrix below. I can make the whole scrollbar transparent except on hover. The labels are right up against the data, which I like, however, upon hover, unless I shift the vertical scroll (which I'd rather not do), the scrollbar obscures the beginning of all the labels.
I would like to set the background of the scrollbar as transparent so that only the "grabber" (or whatever it's called) is the only thing that is drawn. (It will obscure the beginning of the labels it is over, but would be a lot less so.)
Is there any way to do that? Here is what I tried:
Color bg = new Color(255,255,255,0);
colLabelScroll.setBackground(bg);
This does not seem to make the background of the scrollbar transparent.
What I'm shooting for is like how the iPhone's scrollbar grabber hovers over info in some apps. Is that even possible with JScrollBars?
Transparent JScrollBar can do it, but consider this: if column labels are related to the data and you can scroll them independently, beginner users may not understand what is going on and associate column labels with whatever is visually aligned beneath it. Either you will need some sort of visual indicator that makes it clear that the labels are disconnected from the data, or you should change the way labels are scrolled that never leaves them statically in 1 place.
Here's how I ended up making the relationship between the labels and the data clearer:
Instead of allowing the user to independently and intentionally scroll the labels, I decided to control the label scroll position via mouse hover. This eliminates the need for the obtrusive scrollbar.
I created a scroll-bar-like indicator that shows the portion of the data the labels represent.
I highlighted the currently hovered label that corresponds to the data below it, i.e. the only label that is ever correctly aligned with the data is the one that is under (or directly above) the cursor.
When the mouse is not hovered over (or dragging from) the column labels, do not display any labels. This helps prevent invalid label/data associations by the user.
A few nuanced notes: Implementing your own scrollbar-like indicator is somewhat involved, especially if your labels are painted and then rotated, because the paint position of 0 is at the bottom of the pane, yet the vertical scroll position of the pane is at the top. You will have to track the vertical scroll position to be able to recover it again when the cursor returns since you are blanking the labels on mouse out.
When developing a plugin for IntelliJ, I accomplished it with:
scrollPane.getVerticalScrollBar().setUI(ButtonlessScrollBarUI.createTransparent());
It takes advantage of the the:
ButtonlessScrollBarUI.createTransparent()
method, which is an IntelliJ specific method. However, if you can find a ScrollBarUI which has a transparent background, you can use the same trick.
Since I got a bit lost myself at first after reading #hepcat72's answer I'm posting a little explanation about the BasicScrollBarUI class:
JScrollBar scrollbar = scrollPaneConversation.getVerticalScrollBar();
scrollbar.setUI(new BasicScrollBarUI(){
// This function returns a JButton to be used as the increase button
// You could create your own customized button or return an empty(invisible) button
#Override
protected JButton createIncreaseButton(int orientation){
}
// Same as above for decrease button
#Override
protected JButton createDecreaseButton(int orientation){
}
// This function paints the "track" a.k.a the background of the scrollbar
// If you want no background just return from this function without doing anything
// If you want a custom background you can paint the 'Graphics g' object as you like
#Override
protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds)
{
}
// This function paints the "thumb" a.k.a the thingy that you drag up and down
// You can override this function to paint it as you like
#Override
protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)
{
}
});
Refer to the Transparent JScrollBar link posted by #hepcat72 for hints about what to do exactly in these functions.

Stop Swing components relocating to initial coordinates upon ChangeEvent?

I have some movable objects (JLabels) and the user will reposition them within the parent ChartPanel during runtime. To move the objects I've implemented the (really helpful) ComponentMover class, found here: http://tips4java.wordpress.com/2009/06/14/moving-windows/).
As they are moved, their (x,y) coordinates are displayed back to the user. I'm using a JTabbedPane and the coordinates of two objects are displayed per tab (I tried to post a screenshot but my lack of reputation won't allow it).
The problem is when I change tabs, the objects reposition themselves to the coordinates I had initially set them to. I think this is something to do with revalidate() or repaint()?
I tried to implement a ChangeListener
public static final ChangeListener
relocateObjects = new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
labelPole.setAlignmentX(polePositionX);
labelPole.setAlignmentY(polePositionY);
labelZero.setAlignmentX(zeroPositionX);
labelZero.setAlignmentY(zeroPositionY);
}
};
on the JTabbedPane
tabbedPane.addChangeListener(ComponentMover.relocateObjects);
to relocate the object to coordinates stored just before the event, but to no avail.
I tried to find a way to prevent the objects from listening (and thus responding) to a state change or simply update their default positions to override their initial coordinates, which you'd think would be an option, but I have failed.
I've literally spent all day on this, so I would love a solution or a push in the right direction. Thank you.
The problem is when I change tabs, the objects reposition themselves to the coordinates I had initially set them to.
You need to use a null layout on the panel that contains the components you are dragging:
panel.setLayout( null );

changing text of jLabel in a draggable jPanel

In my program, I have a jPanel with a jLabel inside of it. I added this code to make the jPanel draggable, and it works perfectly.
private void formMousePressed(java.awt.event.MouseEvent evt) {
prevX = evt.getXOnScreen();
}
private void formMouseDragged(java.awt.event.MouseEvent evt) {
this.setLocation(this.getX() + evt.getXOnScreen() - prevX, this.getY());
prevX = evt.getXOnScreen();
// this.labBirthDate.setText(Integer.toString(this.getX()));
}
However, when I added the commented-out code, which updates the label to show the position of the panel, it's stopped working. Specifically, when I click and drag to move the panel, instead of following the mouse, the panel just sort of stutters, and the text changes to a value of ~10, changing whenever I move the mouse.
Making things ever more confusing, if I instead change it so that it just sets the text to "blah", it doesn't produce the error. As well, if I just set a variable to be equal to this.getX(), it doesn't produce the error. If I then set the label to be the value of that local variable, the error comes back.
Does anyone know why this might be happening? Is there a workaround I can use to get the same effect?
When you invoke the setText() method on the label the revalidate() and repaint() methods are invoked on the label. This will cause the layout manager to be invoked and I'm guessing the layout manager will reset the panel to its default position.
If you want to be able to randomly move components around a screen then you need to use a null layout on the parent of the panel that is being dragged. Once you do this you will also need to manually set the size and location of your components.
You might find the Drag Layout handy to use in this case.

Resize code does not correspond with mouse movement

I have some code to resize a chatpanel dynamically, but it does not move according to the mouse. What happens is the mouse moves at a faster rate than the panel gets resized. For example, how I want it to be, is in any application, when you click on the border between two windows, and drag it, the mouse stays along with the piece you are clicking on, and currently this is not happening. here is my code. please let me know if you need more
public void mouseDragged(MouseEvent e) {
if(getCursor().getType() == Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR).getType()) {
owner.adjust((int)MouseInfo.getPointerInfo().getLocation().getY());
System.out.println("vertpanel: "+(int)MouseInfo.getPointerInfo().getLocation().getY());
}
}
public void adjust(int adjustment) {
int height = this.getHeight();
System.out.println((((double)(adjustment))/height)+":"+(((double)(height-adjustment))/height));
output.setHeightPercent((((double)(adjustment))/height));
output.componentResized(new ComponentEvent(this, 1));
input.setHeightPercent((((double)(height-adjustment))/height));
input.componentResized(new ComponentEvent(this, 2));
}
there is one main panel, a chatpanel, and within it, there are two smaller panels, a chat input and a chat output
Can't tell exactly what you are doing based on your code.
I would suggest that you should NOT be manually setting the dimensions of the output and input coponents. You should let the layout manager determine how to resize each component as the parent container is resized.
So in your resize code you would need to invoke revalidate() on the parent container as it is resized.
Check out Resizing Components. You should be able to use the ComponentResizer class as long as you use setAutoLayout(true).

Dragging a JLabel in a JPanel using Mouse Events

I'm using the MouseInputListener (MouseListener + MouseMotionListener) to drag and drop multiple JLabels in a JPanel. Here is an outline of what I do;
MouseClicked: check if there is any
JLabel within the clicked area of
JPanel; if yes, select it (paint it to
a color,etc). If not, do nothing.
MouseDragged: If a JLabel is selected,
setLocation of that JLabel using
e.getX() and e.getY() of the event. If
nothing is selected, do nothing.
MouseReleased: If a JLabel is
selected, paint it back to its
original color. Select nothing (maybe
null). If not, do nothing.
These are all in JPanel; JPanel implements MouseInputListener.
So here is the problem: When the inital position of a JLabel is 0,0 say I move it to 10,10. And after the mouse release and nothing is selected, when I click on 0,0 it selects that JLabel; however it was supposed to select it if I click at 10,10 because this is its new position.
Now I think this might be because I'm using the wrong coordinates; I've heard that the coordinate values in JPanel are relative, so I have to do a subtraction (i.e. final-initial coordinates) everytime to get the correct coordinates. I did it, but it did not work either. Another possibility might be that Java is storing all the historical X and Y coordinates (so that everytime I click on a previous coord, I select that object) which is purely imagination!
What are your suggestions?
Thanks in advance.
Add the MouseMotionListener to each of the labels instead of adding it to the panel. Then you don't need to determine whether you clicked on a label or not.
See the Component Mover for a general implementation. You would need to customize it to support the coloring requirement.
Edit:
If you add the listener to the panel then the coordinates will always be relative to the panel, not the label, so I'm not sure what the problem is. If you want to find if you clicked on a component then use the Container.getComponentAt(Point) method.
If you need more help then post your SSCCE that demonstrates the problem.

Categories

Resources