Jbutton에서 Jpopupmenu를 보여 주거나 숨기는 것; FocusListener가 작동하지 않습니까?

StackOverflow https://stackoverflow.com/questions/2421914

문제

첨부 된 드롭 다운 스타일 메뉴가있는 jbutton이 필요했습니다. 그래서 나는 jpopupmenu를 가져다가 아래 코드에서 볼 수있는 방식으로 JBUTTON에 첨부했습니다. 그것이해야 할 일은 이것입니다 :

  • 클릭하면 팝업을 보여줍니다
  • 두 번째로 클릭하면 숨기십시오
  • 팝업에서 항목을 선택하면 숨기십시오.
  • 사용자가 화면에서 다른 곳에서 클릭하면 숨기십시오.

이 4 가지가 작동하지만 사용중인 부울 플래그로 인해 사용자가 다른 곳을 클릭하거나 항목을 선택하면 다시 나타나기 전에 버튼을 두 번 클릭해야합니다. 그렇기 때문에 포커스 리스너 (절대 응답하지 않음)를 추가 하여이 경우 깃발을 허위로 설정하려고했습니다.

편집 : 답변 게시물의 마지막 시도 ...

청취자는 다음과 같습니다.

// 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;
  }
 }
});

나는 지금 너무 오랫동안 이것으로 싸우고 있습니다. 누군가 나에게 이것에 무엇이 잘못되었는지에 대한 단서를 줄 수 있다면, 그것은 좋을 것입니다!

감사!

암호:

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);
    }

}
도움이 되었습니까?

해결책

다음은 우아하지는 않지만 해킹에 나쁘지 않은 또 다른 접근법이 있으며, 내가 말할 수있는 한, 작동하는 한. 먼저, 맨 위에는 두 번째 부울을 추가했습니다. showPopup.

그만큼 FocusListener 다음과 같아야합니다.

    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;
        }
    });

그만큼 isShowingPopup 부울은 다른 곳에서는 변경되지 않습니다. 초점을 얻는다면, 그것이 보여지고 초점을 잃으면 그렇지 않다고 가정합니다.

다음으로 ActionListener 버튼의 경우 다릅니다.

   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;
            }
        }
    });

이제 정말 새로운 비트가 온다. 그것은 a입니다 MouseListener 버튼에 :

    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;
        }
    });

원래, mousePressed 메뉴가 집중력을 잃기 전에 호출됩니다 isShowingPopup 버튼을 누르기 전에 팝업이 표시되는지 여부를 반영합니다. 그런 다음 메뉴가 있으면 showPopup 에게 false, 그래서 actionPerformed 메소드가 호출되면 메뉴가 표시되지 않습니다 (마우스가 놓인 후).

이것은 모든 경우에 예상대로 행동했습니다. 메뉴가 표시되고 사용자가 버튼의 마우스를 눌렀지만 외부에서 해제 한 경우 actionPerformed 호출되지 않았습니다. 이것은 그것을 의미했습니다 showPopup 다음에 버튼을 누르면 메뉴가 표시되지 않았습니다. 이것을 고치려면 mouseReleased 방법 재설정 showPopup. 그만큼 mouseReleased 방법이 호출됩니다 actionPerformed, 내가 말할 수있는 한.

나는 결과 버튼을 조금씩 가지고 놀았고, 버튼에 대해 생각할 수있는 모든 일을했고, 예상대로 작동했습니다. 그러나, 나는 사건이 항상 같은 순서로 이벤트가 일어날 것이라고 확신하지 않습니다.

궁극적으로, 나는 이것이 적어도 시도할만한 가치가 있다고 생각합니다.

다른 팁

다음은 Amber Shah의 "Big Hack"제안의 변형입니다. isshowingpopup 깃발이 없으면 ...

방탄은 아니지만 누군가가 팝업을 닫기 위해 엄청나게 느리게 클릭 할 때까지 아주 잘 작동합니다 (또는 매우 빠른 두 번째 클릭으로 다시 열립니다 ...).

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);
 }

}

내가 의견에서 말했듯이, 그것은 가장 우아한 솔루션은 아니지만 끔찍하게 간단하며 98%에서 작동합니다.

제안에 열려!

당신은 사용할 수 있습니다 jpopupmenu.isvisible () 부울 변수 대신 팝업 메뉴의 현재 상태를 확인하십시오.

추가 해 보셨습니까? ComponentListener ~로 JPopupMenu, 당신은 그것이 표시되고 숨겨진시기를 알 수 있도록 isShowingPopup 그에 따라 깃발)? 초점 변화를 듣는 것이 반드시 올바른 접근법인지 확실하지 않습니다.

필요한 것은 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;                     
            }
        });

나는 이것을 당신의 코드에 삽입하고 그것이 작동하는지 확인했습니다.

글쎄, 나는 당신의 모든 코드를 보지 않으면 확신 할 수는 없지만 팝업이 실제로 전혀 집중하지 않을 가능성이 있습니까? 나는 이전에 스윙에서 제대로 집중하지 않는 것에 문제가 있었기 때문에 범인이 될 수 있습니다. 전화 해보세요 setFocusable(true) 메뉴에서 전화합니다 requestFocus() 메뉴가 나타나면

Tikhon Jelvis의 답변을 시도했습니다 (Focuslistener와 Mouselistener의 스마트 조합을 소개). Linux (Java7/GTK)에서는 작동하지 않습니다. :-(

독서 http://docs.oracle.com/javase/7/docs/api/javax/swing/jcomponent.html#requestfocus%28%29 "이 방법의 사용은 그 행동이 플랫폼 의존적이기 때문에 권한이 없다는 점에 유의하십시오."

청취자 호출 순서가 Java7로 변경되었거나 GTK 대 Windows로 변경 될 수 있습니다. 플랫폼 독립이 되려면이 솔루션을 권장하지 않습니다.

BTW : StackOverFlow에 새 계정을 만들어이 힌트를 제공했습니다. 평판 때문에 그의 대답에 대해 언급 할 수없는 것 같습니다. 그러나 편집 할 버튼이있는 것 같습니다. 이 stackoverflow는 매우 재미있는 것입니다. :-)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top