Frage

brauchte ich einen JButton mit einem Drop-Down-Menü Stil angebracht. Also nahm ich einen JPopupMenu und befestigte es an der JButton in der Art und Weise Sie in der unten stehenden Code sehen können. Was es tun muss, ist dies:

  • das Popup zeigen, wenn darauf geklickt
  • es verstecken, wenn ein zweites Mal angeklickt
  • verstecken, wenn ein Element in dem Popup ausgewählt ist
  • versteckt es, wenn der Benutzer irgendwo anders auf dem Bildschirm klickt

Diese 4 Dinge funktionieren, aber wegen der boolean flag Ich verwende, wenn der Benutzer woanders klickt oder ein Element auswählt, ich habe zweimal auf die Schaltfläche klicken, bevor es wieder auftaucht. Deshalb habe ich versucht, einen Focuslistener hinzuzufügen (was absolut nicht reagiert), das zu beheben und die Flagge falsch in diesen Fällen eingestellt.

EDIT: Letzter Versuch in einer Antwort Post ...

Hier sind die Zuhörer: (es ist in einer Klasse JButton erstreckt, so dass der zweite Hörer auf der JButton ist.)

// Show popup on left click.
menu.addFocusListener(new FocusListener() {
 @Override
 public void focusLost(FocusEvent e) {
  System.out.println("LOST FOCUS");
  isShowingPopup = false;
 }

 @Override
 public void focusGained(FocusEvent e) {
  System.out.println("GAINED FOCUS");
 }
});

addActionListener(new ActionListener() {
 @Override
 public void actionPerformed(ActionEvent e) {
  System.out.println("isShowingPopup: " + isShowingPopup);
  if (isShowingPopup) {
   isShowingPopup = false;
  } else {
   Component c = (Component) e.getSource();
   menu.show(c, -1, c.getHeight());
   isShowingPopup = true;
  }
 }
});

Ich habe mit diesem für Art und Weise kämpft nun zu lang. Wenn mir jemand eine Ahnung geben kann, was mit diesem falsch ist, wäre es toll!

Danke!

Code:

public class Button extends JButton {

    // Icon.
    private static final ImageIcon ARROW_SOUTH = new ImageIcon("ArrowSouth.png");

    // Unit popup menu.
    private final JPopupMenu menu;

    // Is the popup showing or not?
    private boolean isShowingPopup = false;

    public Button(int height) {
        super(ARROW_SOUTH);
        menu = new JPopupMenu(); // menu is populated somewhere else

        // FocusListener on the JPopupMenu
        menu.addFocusListener(new FocusListener() {
            @Override
            public void focusLost(FocusEvent e) {
                System.out.println("LOST FOCUS");
                isShowingPopup = false;
            }

            @Override
            public void focusGained(FocusEvent e) {
                System.out.println("GAINED FOCUS");
            }
        });

        // ComponentListener on the JPopupMenu
        menu.addComponentListener(new ComponentListener() {
            @Override
            public void componentShown(ComponentEvent e) {
                System.out.println("SHOWN");
            }

            @Override
            public void componentResized(ComponentEvent e) {
                System.out.println("RESIZED");
            }

            @Override
            public void componentMoved(ComponentEvent e) {
                System.out.println("MOVED");
            }

            @Override
            public void componentHidden(ComponentEvent e) {
                System.out.println("HIDDEN");
            }
        });

        // ActionListener on the JButton
        addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("isShowingPopup: " + isShowingPopup);
                if (isShowingPopup) {
                    menu.requestFocus();
                    isShowingPopup = false;
                } else {
                    Component c = (Component) e.getSource();
                    menu.show(c, -1, c.getHeight());
                    isShowingPopup = true;
                }
            }
        });

        // Skip when navigating with TAB.
        setFocusable(true); // Was false first and should be false in the end.

        menu.setFocusable(true);
    }

}
War es hilfreich?

Lösung

Hier ist ein weiterer Ansatz, der nicht so schlecht wie ein Hack, wenn nicht elegant, und die, soweit ich das beurteilen kann, funktioniert. Zuerst an der Spitze, habe ich eine zweite boolean genannt showPopup.

Die FocusListener muss wie folgt aussehen:

    menu.addFocusListener(new FocusListener() {
        @Override
        public void focusLost(FocusEvent e) {
            System.out.println("LOST FOCUS");
            isShowingPopup = false;
        }

        @Override
        public void focusGained(FocusEvent e) {
            System.out.println("GAINED FOCUS");
            isShowingPopup = true;
        }
    });

Die isShowingPopup boolean bekommt nirgendwo sonst nicht verändert -. Wenn es den Fokus erhält, nimmt sie es gezeigt, und wenn es den Fokus verliert, nimmt sie es nicht

Als nächstes wird die ActionListener auf der Schaltfläche ist anders:

   addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("isShowingPopup: " + isShowingPopup);
            if (showPopup) {
                Component c = (Component) e.getSource();
                menu.show(c, -1, c.getHeight());
                menu.requestFocus();
            } else {
                showPopup = true;
            }
        }
    });

Jetzt kommt die wirklich neue Bit. Es ist ein MouseListener auf die Schaltfläche:

    addMouseListener(new MouseAdapter() {
        @Override
        public void mousePressed(MouseEvent e) {
            System.out.println("ispopup?: " + isShowingPopup);
            if (isShowingPopup) {
                showPopup = false;
            }
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            showPopup = true;
        }
    });

Im Grunde mousePressed aufgerufen wird, bevor das Menü des Fokus verliert, so isShowingPopup reflektiert, ob das Popup angezeigt wurde, bevor die Taste gedrückt wird. Wenn dann das Menü dort ist, haben wir gerade eingestellt showPopup false, so dass die actionPerformed Methode nicht das Menü nicht zeigen, sobald sie aufgerufen wird (nachdem die Maus losgelassen wird).

Das in jedem Fall wie erwartet verhielten sich aber ein: wenn das Menü wurde zeigt, und der Benutzer betätigt die Maus auf den Button aber veröffentlichte es außerhalb davon wurde actionPerformed nie genannt. Dies bedeutete, dass showPopup blieb falsch und das Menü war nicht das nächste Mal, wenn die Taste gedrückt wurde, gezeigt. Um dies zu beheben, setzt die mouseReleased Methode showPopup. Die mouseReleased Methode wird nach actionPerformed genannt, soweit ich das beurteilen kann.

Ich spielte mit der resultierenden Taste für ein bisschen, all die Dinge, die ich von der Taste denken konnte, und es funktionierte wie erwartet. Aber ich bin nicht 100% sicher, dass die Ereignisse immer in der gleichen Reihenfolge geschehen werden.

Letztendlich denke ich, das ist zumindest ein Versuch wert.

Andere Tipps

Hier ist eine Variante von Amber Shahs „großen Hack“ Vorschlag, den ich gerade gemacht. Ohne die isShowingPopup Flagge ...

Es ist nicht kugelsicher, aber es funktioniert ganz gut, bis jemand mit einem unglaublich langsam Klick kommt das Popup (oder einen sehr schnellen zweiten Klick wieder zu öffnen, es ...) zu schließen.

public class Button extends JButton {

 // Icon.
 private static final ImageIcon ARROW_SOUTH = new ImageIcon("ArrowSouth.png");

 // Popup menu.
 private final JPopupMenu menu;

 // Last time the popup closed.
 private long timeLastShown = 0;

 public Button(int height) {
  super(ARROW_SOUTH);
  menu = new JPopupMenu(); // Populated somewhere else.

  // Show and hide popup on left click.
  menu.addPopupMenuListener(new PopupMenuListener() {
   @Override
   public void popupMenuWillBecomeInvisible(PopupMenuEvent arg0) {
    timeLastShown = System.currentTimeMillis();
   }
   @Override public void popupMenuWillBecomeVisible(PopupMenuEvent arg0) {}
   @Override public void popupMenuCanceled(PopupMenuEvent arg0) {}
  });
  addActionListener(new ActionListener() {
   @Override
   public void actionPerformed(ActionEvent e) {
    if ((System.currentTimeMillis() - timeLastShown) > 300) {
     Component c = (Component) e.getSource();
     menu.show(c, -1, c.getHeight());
    }
   }
  });

  // Skip when navigating with TAB.
  setFocusable(false);
 }

}

Wie ich in den Kommentaren gesagt, das ist nicht die eleganteste Lösung, aber es ist schrecklich einfach und es funktioniert in 98% der Fälle.

Offen für Vorschläge!

Sie könnten die JPopupMenu.isVisible () statt Ihrer Boolesche Variable den aktuellen Zustand der Popup-Menüs zu überprüfen.

Haben Sie versucht, eine ComponentListener zum JPopupMenu hinzufügen, so dass Sie wissen, wann es gezeigt und versteckt (und Ihre isShowingPopup Flagge entsprechend aktualisieren)? Ich bin nicht sicher für Fokus Änderungen hören ist unbedingt der richtige Ansatz.

Was Sie brauchen, ist ein PopupMenuListener:

        menu.addPopupMenuListener(new PopupMenuListener() {

            @Override
            public void popupMenuWillBecomeVisible(PopupMenuEvent arg0) {

            }

            @Override
            public void popupMenuWillBecomeInvisible(PopupMenuEvent arg0) {
                System.out.println("MENU INVIS"); 
                isShowingPopup = false;     
            }

            @Override
            public void popupMenuCanceled(PopupMenuEvent arg0) {
                System.out.println("MENU CANCELLED"); 
                isShowingPopup = false;                     
            }
        });

I eingefügt dies in Ihren Code und überprüft, dass es funktioniert.

Nun, ich kann nicht sicher sein, ohne die gesamten Code zu sehen, aber es ist möglich, dass das Pop-up nie bekommt eigentlich überhaupt konzentrieren? Ich habe Probleme gehabt mit den Dingen nicht vor dem Fokus richtig in Swing bekommen, so dass es könnte die Ursache sein. Versuchen Sie fordern setFocusable(true) auf dem Menü und dann requestFocus() aufrufen, wenn Sie das Menü erscheinen zu lassen.

Ich habe versucht, die Antwort von Tichon Jelvis (eine intelligente Kombination von focuslistener und mouselistener Einführung). Es ist nicht für mich auf Linux (Java7 / gtk) arbeiten. : - (

http: / /docs.oracle.com/javase/7/docs/api/javax/swing/JComponent.html#requestFocus%28%29 Sie haben geschrieben: „Beachten Sie, dass die Anwendung dieser Methode abgeraten, da sein Verhalten Plattform abhängig. "

Es kann sein, dass die Reihenfolge der Zuhörer Anrufe mit Java7 geändert oder es ändert sich mit GTK vs Sie Windows. Ich würde diese Lösung nicht empfehlen, wenn Sie plattformunabhängig sein soll.

BTW: Ich habe ein neues Konto auf Stackoverflow diesen Hinweis zu geben. Es scheint, ich bin nicht auf seine Antwort äussern darf (wegen des Rufes). Aber es scheint, ich habe eine Schaltfläche zu bearbeiten. Dieser Stackoverflow ist eine sehr lustige Sache. : -)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top