简体   繁体   中英

Problems with swing components and awt events

I seem to be having problems with my java gui code, and I have no idea why it's not working.

What needs to happen is when the mouse is clicked on the panel or frame - for now lets just say panel; as this is just a test eventually this code will be implemented for another gui component, but I'd like to get this working first - the popup menu needs to become visible and the focus needs to be set on the text field . Then when the user presses enter or the focus on the text field is lost then the popup menu needs to hide and the text reset to blank or whatever I need it to be.

So this is what I wrote:

public class Test {
    private final JFrame frame = new JFrame();
    private final JPanel panel = new JPanel();
    private final JPopupMenu menu = new JPopupMenu();
    private final JTextField field = new JTextField();
    private final Obj obj;

    //... constructor goes here

    public void test(){
        frame.setSize(new Dimension(200,200));
        field.setColumns(10);
        field.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent arg0) {
                obj.method(field.getText());
                menu.setVisible(false);
                field.setText("");
            }
        });
        field.addFocusListener(new FocusListener() {
             public void focusLost(FocusEvent e) {
                 menu.setVisible(false);
                 field.setText("");
             }

             //... focus gained event goes here
        });
        panel.addMouseListener(new MouseListener() {
            public void mouseClicked(MouseEvent e) {
                menu.setLocation(e.getX(), e.getY());
                menu.setVisible(true);
                field.requestFocusInWindow();
            }

            //... other mouse events go here
        });

        menu.add(field);
        frame.getContentPane().add(panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

With the code as it is written here the menu automatically hides right after I click. It just flashes on screen briefly and then hides without me doing anything else.

If I change the code to exclude any occurrences of menu.setVisible(false) then the text field will never gain focus.

Is this due to a misuse of JPopupMenu? Where am I going wrong?

Also note that I left out main or Obj. They are in another file and most likely insignificant to this problem. Obj.method() does nothing and main only calls Test's constructor and test() method.

This code should work the way you want it to work (hopefully you follow the use of anonymous classes:

public class Test {

public static void main(String[] args) {
    Test test = new Test();
    test.test();
}

private JFrame frame;
private JPanel panel;
private JPopupMenu menu;
private JTextField field;

public Test() {
    frame = new JFrame();
    frame.setSize(new Dimension(200, 200));
    frame.getContentPane().add(getPanel());
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

private JPanel getPanel() {
    if (panel == null) {
        panel = new JPanel();
        panel.setComponentPopupMenu(getMenu());
        panel.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
                menu.show(panel, e.getX(), e.getY());
            }
        });
    }
    return panel;
}

private JPopupMenu getMenu() {
    if (menu == null) {
        menu = new JPopupMenu() {

            @Override
            public void setVisible(boolean visible) {
                super.setVisible(visible);
                if (visible) {
                    getField().requestFocus();
                }
            }
        };
        menu.add(getField());
    }
    return menu;
}

private JTextField getField() {
    if (field == null) {
        field = new JTextField();
        field.setColumns(10);
        field.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                getMenu().setVisible(false);
            }
        });
    }
    return field;
}

public void test() {
    frame.setVisible(true);
}
}

The key things to notice are when we setup the pop-up menu:

panel.setComponentPopupMenu(getMenu());
panel.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseClicked(MouseEvent event) {
        getMenu().show(getPanel(), event.getX(), event.getY());
    }
});

And how you request focus for the text field when the pop up menu is visible, which is accomplished through the text field requesting focus, but not focus in window since it doesn't exist in the window, only in the menu:

menu = new JPopupMenu() {
    @Override
    public void setVisible(boolean visible) {
        super.setVisible(visible);
        if (visible) {
            getField().requestFocus();
        }
    }
};

And finally how the text field dismisses the pop up menu:

field.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        getMenu().setVisible(false);
    }
});

You should be able to do this by overriding getComponentPopupMenu to return a JPopupMenu. This should handle exactly like you want. It will allow for focus, etc.

EDIT: This is not strictly necessary, although it does allow better for inheritance.

Public JPopupMenu getComponentPopupMenu() {
    return getMenu();
}

Oh, and if you want it to show up on any mouse click, add a mouse listener, and call show on the popupmenu:

public void processMouseEvent(MouseEvent e) {
    popup.show(this, e.getX(), e.getY());
}

That will show it on any mouse click.

or, another option is if you have a mouse listener (calling processMouseEvent) and you only want to call on right-click:

public void processMouseEvent(MouseEvent e) {
    if (e.isPopupTrigger()) {
        popup.show(this, e.getX(), e.getY());
    }
}

The mouse listener would look like this:

panel.addMouseListener(new MouseAdapter() {
    mouseClicked(MouseEvent e) {
        processMouseEvent(e);
    }
}

I'd like to point out that I'm discovering through usage of the suggested methods that setComponentPopupMenu() automatically adds a MouseListener to display the given PopupMenu which then consumes the right-click event.
so whatever is inside the if(e.isPopupTrigger()) structure is never run on right clicks because the event is consumed.

So essentially I get the behavour specified in my question just by adding panel.setComponentPopupMenu(getMenu()) , but the fact that it's consuming all my right-click events, not just mouseClicked, is extremely limiting.

When you show a popup it should have the focus (and it probably grabs it) anything else really doesn't make to much sense.

So what's probably happens is this: the Menu shows, and grabs the focus.

with the next command you travers the focus to the textfield. Since the popup has no focus and a popup without focus is useless, it hides again.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM