如何在窗口外单击鼠标关闭Vaadin子窗口?

时间:2013-12-02 10:55:53

标签: window vaadin vaadin7

我正在使用vaadin 7,在我的应用程序中,我有时会使用子窗口。在一个案例中,我有一个模态窗口,里面有几个组件。单击模态窗口内的某些组件时,它会打开另一个窗口。当用户点击它时,我希望此窗口自动关闭(例如,再次在模态窗口上)。在Vaadin Sampler中,这种行为似乎在显示源时实现(单击右上角的源按钮)。如果没有从模态窗口打开,行为应该是相同的,但是从UI或任何其他子窗口打开。

我尝试了几件事:

  • 使用Popupview是不可能的,因为我需要从组件(按钮或图像)打开窗口

  • 将BlurListener添加到新窗口不起作用,因为如果我在窗口内单击会触发blurevent(例如移动窗口)

  • 将ClickListener添加到UI无济于事,因为单击模态窗口时未触发事件。

实现这一目标的正确方法是什么?

由于 拉斐尔

6 个答案:

答案 0 :(得分:4)

我遇到了同样的问题,对任何答案都不满意:

  1. @Steven Spungin提出的聚焦/模糊方法确实有效,但前提是窗口内没有其他可聚焦元素。否则,单击其中一个元素将关闭窗口,这是不可取的。
  2. @Steven Spungin使用“玻璃元素”为他的第一个答案提出了解决方案,但它仅适用于模态窗口,并且通常不是很灵活。
  3. 通过@dwi wahyu utomo回答,他建议在UI.getCurrent().addClickListener添加点击监听器,并检查点击事件坐标是否在窗口内,有效,但是有一个主要问题:点击事件是“消耗的” “由Vaadin提供并且不会传播到浏览器。这意味着点击页面上的任何位置都不会导致正常行为,例如右键单击不会显示本机上下文菜单。
  4. 我想到了一个基于客户端连接器创建简单AbstractExtension的想法,该连接器扩展了特定组件并侦听页面上的所有点击事件。如果单击的目标不在扩展组件内,则通知服务器端连接器。

    这是客户端连接器:

    @Connect(ClickOutsideComponentExtension.class)
    public class ClickOutsideComponentConnector extends AbstractExtensionConnector implements NativePreviewHandler {
    
        private ComponentConnector extendedConnector;
        private ClickOutsideComponentRpc rpc;
        private HandlerRegistration handlerRegistration;
    
        @Override
        protected void extend(ServerConnector target) {
            extendedConnector = (ComponentConnector) target;
            rpc = getRpcProxy(ClickOutsideComponentRpc.class);
            handlerRegistration = Event.addNativePreviewHandler(this);
        }
    
        @Override
        public void onUnregister() {
            super.onUnregister();
            handlerRegistration.removeHandler();
        }
    
        @Override
        public void onPreviewNativeEvent(NativePreviewEvent event) {
            if (extendedConnector.isEnabled()) {
                Element eventTarget = Element.as(event.getNativeEvent().getEventTarget());
                if (Event.ONCLICK == event.getTypeInt() && !isElementInsideExtendedElement(eventTarget)) {
                    rpc.onClickOutside();
                }
            }
        }
    
        public boolean isElementInsideExtendedElement(Element element) {
            Element outsideElement = extendedConnector.getWidget().getElement();
            Element insideElement = element;
    
            while (insideElement != null) {
                if (outsideElement.equals(insideElement)) {
                    return true;
                }
                insideElement = insideElement.getParentElement();
            }
            return false;
        }
    
    }
    

    用于客户端和服务器端之间通信的RPC:

    public interface ClickOutsideComponentRpc extends ServerRpc {
        void onClickOutside();
    }
    

    和服务器端扩展:

    public class ClickOutsideComponentExtension extends AbstractExtension {
    
        private List<ClickOutsideListener> clickOutsideListeners = new ArrayList<>();
    
        public interface ClickOutsideListener extends Serializable {
            void onClickOutside();
        }
    
        @Override
        public void extend(AbstractClientConnector target) {
            super.extend(target);
            registerRpc(new ClickOutsideComponentRpc() {
    
    
                @Override
                public void onClickOutside() {
                    for (ClickOutsideListener listener : clickOutsideListeners) {
                        if (listener != null) {
                            listener.onClickOutside();
                        }
                    }
                }
            });
        }
    
        public void addClickOutsideListener(ClickOutsideListener listener) {
            clickOutsideListeners.add(listener);
        }
    }
    

    如前所述,此解决方案适用于任何组件之外的点击,因此您可以执行以下操作:

    Label label = new Label("Try to click outside!");
    ClickOutsideComponentExtension ext = new ClickOutsideComponentExtension();
    ext.extend(label);
    ext.addClickOutsideListener(new ClickOutsideListener() {
    
        @Override
        public void onClickOutside() {
            Notification.show("Click outside of label");
        }
    });
    addComponent(label);
    

    或点击窗外点击窗口:

    Button btn = new Button("Open window");
    btn.addClickListener(new ClickListener() {
    
        @Override
        public void buttonClick(ClickEvent event) {
            Window w = new Window();
            w.setContent(new Button("Focusable button"));
            w.center();
            ClickOutsideComponentExtension ext = new ClickOutsideComponentExtension();
            ext.extend(w);
            ext.addClickOutsideListener(new ClickOutsideListener() {
    
                @Override
                public void onClickOutside() {
                    w.close();
                }
            });
            UI.getCurrent().addWindow(w);
        }
    });
    addComponent(btn);
    

答案 1 :(得分:2)

如果窗口是模态的,这可能会有所帮助:

public void showWindow() {
    final Window window = new Window();
    Button closeButton = new Button("\u00a0"); // &nbsp;
    closeButton.addClickListener(new ClickListener() {
        private static final long serialVersionUID = 1L;

        @Override
        public void buttonClick(ClickEvent event) {
            window.close();
        }
    });
    closeButton.addStyleName(BaseTheme.BUTTON_LINK);
    closeButton.addStyleName("my-style");
    window.setContent(new VerticalLayout(closeButton));
    window.setModal(true);
    window.setWidth("300px");
    window.setHeight("150px");
    UI.getCurrent().addWindow(window);
}

用这个css:

.v-button.v-button-my-style {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: -5;
}

答案 2 :(得分:2)

如果窗口有固定位置和大小,则:

public MyWindow extends Window {
 private int x=0,y=100,width=300,height=500; 
 ...

 // constructor
 public MyWindow() {
  ...
  UI.getCurrent().addClickListener(MouseEvents.ClickListener() {
   @Override
   public void click(com.vaadin.event.MouseEvents.ClickEvent event) {
    if(!(event.getRelativeX()>=x && event.getRelativeX()<(x+width) && 
     event.getRelativeY()>=y && event.getRelativeY()<(y+height))) {
     hide();
    }
   }
  };
  ...
 }

 // showing window
 public void show() {
  if(getParent()==null) {
   setPosition(x,y);
   setWidth(width,Unit.PIXELS);
   setHeight(height,Unit.PIXELS);
   UI.getCurrent().addWindow(this);
  }
 }

 // hiding window
 public void hide() {
  if(getParent()!=null) {
   UI.getCurrent.removeWindow(this);
  }
 }

 ...
}

希望能解决您的问题

答案 3 :(得分:2)

试试这个:

Window window = new Window();
window.setModal(true);
window.addBlurListener(event -> window.close())

在外部点击后触发模糊事件之前,您的窗口将需要焦点;在调用focus()之前,您需要先打开它,因为Vaadin在这方面并不重视。

window.show();
window.focus();

答案 4 :(得分:0)

你提到的Vaadin Sampler的行为依赖于非模态的窗口:

 window.setModal(false); 

在这种情况下,您可以将单击侦听器添加到窗口(或UI /布局下),wchich关闭子窗口,例如。

 window.addClickListener(new MouseEvents.ClickListener() {

        @Override
        public void click(MouseEvents.ClickEvent event) {
            childWindow.close();         
        }
    });

当窗口是模态时,其他组件不可访问,因此可能没有简单的方法来定义当前窗口之外可能被触发的任何点击事件。

答案 5 :(得分:0)

这是另一种适用于具有可聚焦控件的模态窗口的方法。

在Vaadin中打开模态窗口后,UI不会收到任何点击事件,但是Window会这样做。但是,客户端不会向服务器发送单击次数。

您需要将窗口扩展为在玻璃上获得的点击次数。单击玻璃时,然后关闭窗口。

注意:我们假设Vaadin如何创建Window元素以获取Glass元素。最有可能是更好的方法。简而言之,我们等待窗口连接,然后等待100毫秒以便玻璃连接,然后在玻璃上安装一个点击处理程序。

点击处理程序

public interface ClickHandler extends ServerRpc {
    void onClick(String elementId);
}

Extender - 服务器端

public class GlassClickExtender extends AbstractExtension {
    public GlassClickExtender(Window window, ClickHandler clickHandler) {
        extend((AbstractClientConnector) window);
        registerRpc(clickHandler);
    }
}

Extender - GlassClickExtenderClient

@Connect(GlassClickExtender.class)
public class GlassClickExtenderClient extends AbstractExtensionConnector {

private ClickHandler clickHandler;

@Override
protected void init() {
    clickHandler = getRpcProxy(ClickHandler.class);
    super.init();
}

@Override
protected void extend(ServerConnector serverConnector) {
    try {
        final Widget widget = ((ComponentConnector) serverConnector).getWidget();

        widget.addAttachHandler(new AttachEvent.Handler() {
            @Override
            public void onAttachOrDetach(AttachEvent event) {
                if (event.isAttached()) {
                    new Timer() {
                        @Override
                        public void run() {
                            Element windowElement = widget.getElement();
                            final Element glass = (Element) windowElement.getPreviousSibling();
                            if (glass == null || !glass.getClassName().contains("v-window-modalitycurtain")) {
                                return;
                            }
                            Event.sinkEvents(glass, Event.ONCLICK);
                            Event.setEventListener(glass, new EventListener() {

                                @Override
                                public void onBrowserEvent(Event event) {
                                    if (Event.ONCLICK == event.getTypeInt()) {
                                        clickHandler.onClick(glass.getId());
                                    }
                                }
                            });
                        }
                    }.schedule(100);
                }
                ;
            }

            ;
        });

    } catch (Exception e) {
        System.out.print(e.getMessage());
    }
}

}

现在,扩展窗口并聆听点击

 new GlassClickExtender(window, elementId -> {
                    close();
                });