I have written a DragAndDrop MouseListener.
Component A is a "background" image that is behind Component B. Both are located on a JPanel.
I have made the image draggable. However, I want the image to remain behind component B as I drag it.
However, every time I drag the image, I suppose Java gives it focus or something, so it gets brought to the forefront.
Is there a method that can keep the image in the back even as I am dragging it?
I know I can use a JLayeredPane and use the moveToBack method every time I drag, but I would rather not use a JLayeredPane and just use a JPanel. Is there a moveToBack equivalent for JPanel?
Or is there a way to make the component preserve the current layer (maybe "not gain focus") so that I can drag it within its current layer?
HERE IS AN EXAMPLE
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class OverlapTester extends JFrame {
public static final long serialVersionUID = 172L;
public static void main(String[] args) {
OverlapTester frame = new OverlapTester();
frame.initialize();
}
public void initialize() {
setLayout(null);
JButton bottom = new JButton("bottom");
JButton top = new JButton("top");
bottom.setBounds(0,0,100,100);
top.setBounds(0,0,50,50);
add(top);
add(bottom);
int bottomZOrder = 0;
bottom.addMouseListener(new MouseListener(){
#Override
public void mouseEntered(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
#Override
public void mouseExited(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
#Override
public void mouseReleased(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
#Override
public void mousePressed(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
#Override
public void mouseClicked(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
});
bottom.addMouseMotionListener(new MouseMotionListener(){
#Override
public void mouseDragged(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
#Override
public void mouseMoved(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
});
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setExtendedState(MAXIMIZED_BOTH);
setVisible(true);
}
}
Component A is a "background" image that is behind Component B. Both are located on a JPanel.
Swing components are painted based on their ZOrder. The highest ZOrder is painted first.
The ZOrder is assigned as a component is added to the panel. So the first component added is given ZOrder 0, and the second component ZOrder 1.
So add your "background" image to the panel last and it will always have the highest ZOrder which means it is painted first, so other components will be painted on top of it.
For example:
panel.add(someOtherComponent);
panel.add(background);
Or you can use the:
Container.setComponentZOrder(...)
method to change the ZOrder dynamically after a component has been added.
Edit:
The answer I provided was you was an either/or solution. You only need to choose one approach.
You implemented the first approach correctly, which is to add the "background" component last. So there is no need to then play with the ZOrder. This approach works great when ZOrder will remain fixed.
The second approach is when you want the ZOrder to dynamically change, say when you have multiple components and you want to change it as you interact with each component. However you implemented this approach incorrectly. You used "0" for the ZOrder, which means the component will always be painted last and therefore on on top of any other component.
In your current example there is no need to change the ZOrder dynamically, so you can remove all related code.
As you can see the bottom button is consistently being added to the forefront
Swing painting is optimized to assume components don't overlap.
However, in the case of a JButton, it has automatic repaint logic when you hover over the button to repaint the Border. So when the button is repainted it still paints over top of the other component.
So you need to tell Swing to NOT optimize the painting. Therefore when one component is repainted they will all be repainted when they overlap. You do this by overriding the isOptimizedDrawingEnabled(...) method as demonstrated below:
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JButton;
import javax.swing.*;
public class OverlapTester extends JFrame {
public static void main(String[] args) {
OverlapTester frame = new OverlapTester();
frame.initialize();
}
public void initialize() {
JPanel contentPane = new JPanel(null)
{
#Override
public boolean isOptimizedDrawingEnabled()
{
return false;
}
};
add(contentPane);
// setLayout(null);
JButton bottom = new JButton("bottom");
JButton top = new JButton("top");
bottom.setBounds(0,0,100,100);
top.setBounds(0,0,50,50);
// add(top);
// add(bottom);
contentPane.add(top);
contentPane.add(bottom);
// int bottomZOrder = 0;
int bottomZOrder = 1;
bottom.addMouseListener(new MouseListener(){
#Override
public void mouseEntered(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
#Override
public void mouseExited(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
#Override
public void mouseReleased(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
#Override
public void mousePressed(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
#Override
public void mouseClicked(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
});
bottom.addMouseMotionListener(new MouseMotionListener(){
#Override
public void mouseDragged(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
#Override
public void mouseMoved(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
});
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setExtendedState(MAXIMIZED_BOTH);
setVisible(true);
}
}
Related
Kind of a noob question, but then again, I am a noob. I'm trying to implement a sort of "universal" mouse listener. That is, when I click any of the objects on screen, it runs a specific amount of code. I have the current solution below, but the code I want to run is the same for 10 different objects, so this gets rather tedious.
difference2 = new JLabel(new ImageIcon("transparent.png"));
difference2.setBounds(645,490,10,10); //left, top, width, height
contentPane.add(difference2);
difference2.setVisible(true);
difference2.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e) {
//code
}
});
I am aware I can create a separate method such as the following
public void mouseClicked(MouseEvent e) {
JOptionPane.showMessageDialog(null,"this would be nice");
}
But I can't figure out how to set up a mouse listener on every object for it. The JOptionPane currently does nothing.
I might have misread your question, but if you want the same mouselistener on various objects,you could store the instance of your listener in a variable once and then add it to whatever gui object you want it added to.
MouseListener ml = new MouseListener() {
#Override
public void mouseReleased(MouseEvent e) {//code}
#Override
public void mousePressed(MouseEvent e) {//code}
#Override
public void mouseExited(MouseEvent e) {//code}
#Override
public void mouseEntered(MouseEvent e) {//code}
#Override
public void mouseClicked(MouseEvent e) {//code}
};
JLabel j1 = new JLabel("Label1");
j1.addMouseListener(ml);
JLabel j2 = new JLabel("Label2");
j2.addMouseListener(ml);
You can create an instance of an anonymous class that extends MouseAdapter and assign it to a variable that you can reuse (myMouseListener in this case):
MouseListener myMouseListener = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
JOptionPane.showMessageDialog(null,"this would be nice");
}
};
difference2.addMouseListener(myMouseListener);
aSecondObject.addMouseListener(myMouseListener);
aThirdObject.addMouseListener(myMouseListener);
...
I´m using KeyBindings and MouseListener to react on keyboard/mouse inputs. Many mouses have a back/forward button (for example to go a page back and forth in a webbrowser). How can I react on these buttons in Java?
Special mouse keys are usually bound by the mouse's vendor software to virtual keystrokes. Try implementing a KeyListener, set a breakpoint inside the keyPressed method, debug and watch which keyCode do you get when you press a special button on your mouse. This way, you would likely also handle special keys on keyboards which often also provide the same functionality.
Just implement a MouseListener and have a look at the mousePressed() event.
Quick and dirty program to test mouse buttons:
package stackoverflowanswer;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class MouseListenerApp{
public static void main(String[] args) {
Runnable r = new Runnable(){
#Override
public void run() {
JFrame frame = new JFrame("mouselistener");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
System.out.println(e.getButton());
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
});
frame.setSize(200,200);
frame.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
}
For me forward/backward corresponds to key 4/5.
i have to check if i pressed left, right or both buttons on my mouse, here is a sample code:
package sandbox;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Sandbox extends JFrame {
public static void main(String[] args) {
Sandbox s = new Sandbox();
s.setVisible(true);
}
public Sandbox() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(300, 200);
setResizable(false);
setTitle("Frame");
setLayout(null);
JButton but = new JButton("click me");
but.setBounds(0, 0, 120, 50);
but.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
if(SwingUtilities.isLeftMouseButton(e) | SwingUtilities.isRightMouseButton(e))
System.out.println("both");
else if(SwingUtilities.isLeftMouseButton(e))
System.out.println("left");
else if(SwingUtilities.isRightMouseButton(e))
System.out.println("right");
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
});
add(but);
}
}
i have to make it using SwingUtilities, but there is a problem if i press left or right single button it will print "both", not "left" or "right", if i used MouseEvent class it was working properly but i need it with SwingUtilities class if it's possible, thanks.
EDIT: using MouseEvent class it's working right:
if(e.getModifiersEx() == (MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK))
...
Don't check using OR (|) but use AND (& or better the short cut version &&) instead:
if(SwingUtilities.isLeftMouseButton(e) && SwingUtilities.isRightMouseButton(e))
...
Update:
For checking if both buttons are pressed, SwingUtilities doesn't seem to be the right tool. The isXxxxMouseButton(...) methods do the same as you do, they check the button masks, but use different masks which seem not be set when both buttons are pressed.
As an example, isRightMouseButton() is implemented as follows:
public static boolean isRightMouseButton(MouseEvent anEvent) {
return ((anEvent.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK);
}
As you can see, the implementation isn't that complext, so you might be better off to just do the mask check yourself.
mouse listene event on button
but.addMouseListener(new MouseListener() {
mouse listener event on fame.
this.addMouseListener(new MouseListener() {
Please check with '||' not '|', don't use the && because it is not possible to press both buttons at the same time
if(SwingUtilities.isLeftMouseButton(e) || SwingUtilities.isRightMouseButton(e))
You will get result
both
left
both
left
both
right
I added one MouseMotionListener to the JTextField. But when I use jf.getMouseMotionListeners().length to know about how many mouse listeners are registered then I get 3!!. That's causing me some problem because I'm trying to add a listener depending on that length. If its zero I add or else I don't want to add a listener.
Below is the code and I have written code to know the length in mouse moved event.
public static void main(String args[]) {
JFrame fr = new JFrame();
final JTextPane jf = new JTextPane ();
jf.addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseMoved(MouseEvent arg0) {
System.out.println(jf.getMouseListeners().length);
}
#Override
public void mouseDragged(MouseEvent arg0) {
// TODO Auto-generated method stub
}
});
jf.setBounds(30,30,100,50);
fr.setSize(new Dimension(500, 500));
fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fr.setLayout(null);
fr.add(jf);
fr.setVisible(true);
}
Why is that length 3 when I added just one listener?
is there a better way to check whether a listener on a component is registered or not?
EDIT
Here's what I'm trying to do.
I should be able to drag the JTextPane any where inside window and I should be able to edit it by double clicking on the JTextPane .
If I have a drag listener while editing and If wan't to select a text to style it the JTextPane gets dragged instead of selecting a text from JTextPane.
Now I want to remove the motionlistener when I am in editing mode and add it when I'm not editing.
So thats why I'm trying to remove it or in simple I need to disable the motionlistener.
output is correct, JTextField has another notifiers implemented API, they are notified from added MouseListener
see whats debuger returns
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class MouseAndJTextField {
private JFrame fr = new JFrame();
private JTextField jf = new JTextField(20);
public MouseAndJTextField() {
jf.addMouseListener(new MouseListener() {
#Override
public void mouseReleased(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseClicked(MouseEvent arg0) {
System.out.println(jf.getMouseListeners().length);
}
});
fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fr.add(jf);
fr.pack();
fr.setVisible(true);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
MouseAndJTextField fs = new MouseAndJTextField();
}
});
}
}
You can define your class (inner class) instead of the Anonymous inner class for the listener and go through the listeners calling instanceof to find whether your instance is added.
Alternatively you can define a flag indicating whether the listener should process event or not. Set the flag by default to true. Set to false when you have to skip event (in your edit mode) and reset after.
I am trying to make a set of navigation buttons for a file browser. I want it so that if the user clicks the dedicated history button, a JPopupMenu appears. However, I also want that exact same menu to appear when the user right-clicks or drags the cursor down the back or forward button. How can I make that exact same JPopupMenu (not a copy, but the same exact one) appear for multiple GUI components for different gestures?
So far I've tried the following:
histButton.addMouseListener(new MouseAdapter()
{
#Override public void mouseClicked(MouseEvent e)
{
showPopup(e);
}
#Override public void mouseDragged(MouseEvent e)
{
showPopup(e);
}
private void showPopup(MouseEvent e)
{
histPopupMenu.show(e.getComponent(), e.getX(), e.getY());
}
});
forwardButton.addMouseListener(new MouseAdapter()
{
#Override public void mouseClicked(MouseEvent e)
{
if (e.isPopupTrigger())
showPopup(e);
}
#Override public void mouseDragged(MouseEvent e)
{
showPopup(e);
}
private void showPopup(MouseEvent e)
{
histPopupMenu.show(e.getComponent(), e.getX(), e.getY());
}
});
backButton.addMouseListener(new MouseAdapter()
{
#Override public void mouseClicked(MouseEvent e)
{
if (e.isPopupTrigger())
showPopup(e);
}
#Override public void mouseDragged(MouseEvent e)
{
showPopup(e);
}
private void showPopup(MouseEvent e)
{
histPopupMenu.show(e.getComponent(), e.getX(), e.getY());
}
});
All components are added and display correctly, and debugging shows me that they register the events, but no menu appears.
Bringing Up a Popup Menu shows the traditional implementation using mousePressed(), mouseReleased() and isPopupTrigger(). Note that "The exact gesture that should bring up a popup menu varies by look and feel." You might compare what's shown with your implementation, which uses mousePressed().
Addendum: For reference, #mKorbel recalls this client property that may prove useful.
import java.awt.Component;
import java.awt.event.*;
import javax.swing.*;
/** #author mKorbel */
public class ComboBoxAction extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
private JComboBox comboBox;
private JFrame frame;
public ComboBoxAction() {
comboBox = new JComboBox();
comboBox.addActionListener(this);
comboBox.addItem("Item 1");
comboBox.addItem("Item 2");
comboBox.addItem("Item 3");
comboBox.addItem("Item 4");
for (Component component : comboBox.getComponents()) {
if (component instanceof AbstractButton) {
if (component.isVisible()) {
comboBox.remove(component);
}
}
}
//This prevents action events from being fired when the
//up/down arrow keys are used on the dropdown menu
comboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
comboBox.firePopupMenuWillBecomeVisible();
frame = new JFrame();
frame.add(comboBox);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(comboBox.getSelectedItem());
//make sure popup is closed when 'isTableCellEditor' is used
comboBox.hidePopup();
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new ComboBoxAction();
}
});
}
}