本帖最后由 houguojun 于 2011-12-14 19:54:53 编辑

解决方案 »

  1.   

    用 getParent() 获得所有 Container,
    加上 addMouseListener() 多数地方是可以,但是不是JComboBox那个效果。
    比如JFame的工具栏,不是Parent树上的,在那些地方就处理不了。跪求高手!
      

  2.   

    addFocusListener(new FocusAdapter(){ public void focusLost(FocusEvent e){ hidePopup();}});
      

  3.   

    addAncestorListener(new AncestorListener(){
        public void ancestorAdded(AncestorEvent event){ hidePopup();}
        public void ancestorMoved(AncestorEvent event){ if (event.getSource() != popup) hidePopup();}
        public void ancestorRemoved(AncestorEvent event){hidePopup();}
    });
      

  4.   

    多谢楼上2位,2位的一起加上,也只能处理焦点丢失\祖先移动等情况.还是没有JComboBox的效果.仍然无法处理区域外鼠标点击\或鼠标滚轮滚动的事件
      

  5.   

    swing设计的监听模型应该是基于面向对象的理念
    监听有一个监听对象,监听对象发生了xx事件,比如发生在监听对象UI范围内或或监听对象相关的事件才能被监听到
    它的监听模型就是这样建立的,在此模型下,除此以外无法直接监控。除非去监听区域外事件发生的相关对象,非swing组件的话,要牵涉到底层系统的事件监听,这个没试过,有兴趣可以研究一下。
      

  6.   

    钩子没有移植性
    这2天看了很多Java底层文档,比如快捷键,系统有一个对象监听,可惜没有看到鼠标的。JComboBox的弹出部分是JPopupMenu,这个源代码看100次也没有收获,但是,事实是它就处理了它本身之外的鼠标事件。
    JPopupMenu鼠标移出本身区域,没有隐藏,即不是监听了鼠标移出事件。继续跪求高手点解!
      

  7.   

    为什么要考虑Java的可移植性?你的软件有多大的几率在Linux下跑?考虑80%的市场行为就行了。不要为了跨平台而跨平台。
      

  8.   

    Swing的我不熟悉,SWT的不用hook也能实现,因为有个全局处理消息的Display类,能得到所有的系统消息,在上面加一个filter就OK了。
      

  9.   

    都是为了好看,唉!
    其实现在就是弹出一个JDialog还不是一样用。
    我是想,人家swing的JPopup都做了,学习下长点见识也不错嘛。
    不想一脚踏进了个深渊,悲剧!
      

  10.   

    我觉得纯java是肯定可以实现的,你应该对swing的事件处理机制还没掌握透彻。我自己就用swt实现了一套自定义的Toolbar, Menubar,PopupMenu.http://feeling.sf.net/images/1.jpg
      

  11.   

    你看过Popup这个类的实现没?我觉得很简单啊。
      

  12.   

    在看,没有发现我说的问题。我在想,是不是还是做的一个FocusLost。
      

  13.   

    不过这个类是 HeavyWeightWindow ,依赖于底层系统的实现,有些东西没有暴露出来。
      

  14.   

    实际上不需要你知道鼠标发生什么事件,这个popup比较特殊,它知道什么时候需要自动关闭,实际上这个是操作系统来控制的,这是个重量级的控件。
      

  15.   

    还有你会debug吗?在 JPopupMenu 里面 设置一些断点,你能看到非常有用的事件信息。JPopupMenu应该给你提供了足够有用的东西。
      

  16.   

    Popup.hide()里设置了断点,什么也没有发生,555
      

  17.   

    BasicPopupMenuUI
         static class MouseGrabber implements ChangeListener,
            AWTEventListener, ComponentListener, WindowListener 
    stateChanged
    cancelPopupMenu line 828
    MouseGrabber.cancelPopupMenu line 855
    要下班了,记个笔记。
      

  18.   


    我的天,饶了天大一个弯,弹一个JPopupMenu,效果就出来了,只是布局还有些问题。有希望了!
      

  19.   

    可以参考swing hacks这本书第十个Hack 10. Building a Drop-Down Menu Button
      

  20.   

    终于搞定了,核心就是重写JPopupMenu的setVisible。
    我把代码贴出来,大家给我指点指点。
    代码还有一个事情没有处理,就是在区域里面滚动鼠标hide popup,休息休息加上去。
      

  21.   


    public class Box extends JLabel implements MouseListener {
    private boolean msIsIn = false; private final JComponent owner = this;
    private final JPanel panel = new JPanel();
    private PopupMenu pm = new PopupMenu(); public Box() {
    this.setBackground(Color.white);
    this.setBorder(new LineBorder(new Color(153, 180, 209), 1, true));
    this.setText("text");
    this.addMouseListener(this); panel.setBorder(new LineBorder(new Color(153, 180, 209), 1, true));
    panel.setBackground(Color.white);
    panel.addMouseListener(this);
    } @Override
    public void mouseClicked(MouseEvent e) {
    Object o = e.getSource();
    if (o == this) {
    if (!pm.isVisible()) {
    pm.setVisible(true);
    } else {
    msIsIn = false;
    pm.setVisible(false);
    }
    msIsIn = true;
    }
    } @Override
    public void mousePressed(MouseEvent e) {
    } @Override
    public void mouseReleased(MouseEvent e) {
    } @Override
    public void mouseEntered(MouseEvent e) {
    Object o = e.getSource();
    if (o == this || o == panel) {
    msIsIn = true;
    }
    } @Override
    public void mouseExited(MouseEvent e) {
    Object o = e.getSource();
    if (o == this || o == panel) {
    msIsIn = false;
    }
    } class Popup {
    JWindow window = null; Popup() {
    Window parent = SwingUtilities.getWindowAncestor(owner);
    window = new JWindow(parent);
    window.getContentPane().setBackground(Color.white);
    window.getContentPane().setLayout(new BorderLayout());
    Point p = owner.getLocationOnScreen();
    p.y += owner.getSize().height;
    window.setLocation(p);
    window.setSize(300, 400);
    window.getContentPane().add(panel, BorderLayout.CENTER);
    } void show() {
    window.setVisible(true);
    } void hide() {
    window.setVisible(false);
    window.dispose();
    }
    } class PopupMenu extends JPopupMenu {
    transient Popup popup; PopupMenu() {
    super(null);
    setInvoker(owner);
    }
    public void setVisible(boolean b) {
    // Is it a no-op?
    if (b == isVisible())
    return;
    System.out.printf("setVisible: [%s,%s]\n", b, msIsIn); // if closing, first close all Submenus
    if (b == false && !msIsIn) {
    Boolean doCanceled = (Boolean) getClientProperty("JPopupMenu.firePopupMenuCanceled");
    if (doCanceled != null && doCanceled == Boolean.TRUE) {
    putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.FALSE);
    firePopupMenuCanceled();
    }
    getSelectionModel().clearSelection(); } else {
    // 这里使得mouse在区域外发送了setVisible(false);
    if (isPopupMenu()) {
    MenuElement me[] = new MenuElement[1];
    me[0] = (MenuElement) this;
    MenuSelectionManager.defaultManager().setSelectedPath(me);
    }
    }
    if (b) {
    firePopupMenuWillBecomeVisible();
    popup = getPopup();
    firePropertyChange("visible", Boolean.FALSE, Boolean.TRUE);
    } else if (popup != null && !msIsIn) {
    firePopupMenuWillBecomeInvisible();
    popup.hide();
    popup = null;
    firePropertyChange("visible", Boolean.TRUE, Boolean.FALSE);
    if (isPopupMenu()) {
    MenuSelectionManager.defaultManager().clearSelectedPath();
    }
    }
    } private Popup getPopup() {
    Popup oldPopup = popup;
    if (oldPopup != null) {
    oldPopup.hide();
    }
    Popup newPopup = new Popup();
    newPopup.show();
    return newPopup;
    } public boolean isVisible() {
    return popup != null;
    } private boolean isPopupMenu() {
    Component invoker = super.getInvoker();
    return ((invoker != null) && !(invoker instanceof JMenu));
    } }}