Dragging a JLabel in a JPanel using Mouse Events - java

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.

Related

Jlabel Extra Clickable Space

I create buttons using jlabels, so I can make a image into sort of a button. The only problem is, is that jlabels are square, therfore if i click somewhere within the square where the picture is not contained, it still runs the jlabel.MouseClickEvent. Is there any fix for this, or another component that i could use?
Ex. If i click this on the corner where the circle is not showing, but the square is still there, then the event fires.
Any fixes/different components to use? Thanks!
If you are just using simple Shapes for the images then you might be able to use the Shape Component found in Playing With Shapes.
The ShapeComponent will only respond to mouse events within the bounds of the Shape.
Otherwise the solution is to override the contains(...) method of your JLabel to check if the mouse point is in the bounds of your image, or in your case if the pixel at that location is not transparent.

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.

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.

How do I make a Swing JComponent a bigger mouse target?

I have a JPanel that contains a bunch of Swing JComponents, including some JSeparators that may be only one or two pixels wide. I want to let my users drag the items around, but it can be rather difficult to hit a one or two pixel wide line. Is there a way that I can give those JSeparators a wider "target" region for mouse clicks? The only thing I've been able to think of is to have my mouse handler listen for clicks on the JPanel, and if it gets any, run through the list of JSeparators, looking to see if any of them are within a couple of pixels of the mouse click.
Should that work? Is there a better way?
Add a fat EmptyBorder to the component.
If it already has a border, you can set a compound border using the current border then the empty border, or simpler, add the empty border (and listener) to a panel that contains the component. The latter will work better for components such as JButton, which have borders that change according to state and focus.

Problem with mouseListener on JPanel

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.

Categories

Resources