5

我正在使用 vaadin 7,在我的应用程序中我有时会使用子窗口。在一种情况下,我有一个模式窗口,其中包含多个组件。当单击模态窗口内的某些组件时,它会打开另一个窗口。我希望这个窗口在用户点击它外部时自动关闭(例如再次在模态窗口上)。在Vaadin 采样器中,这种行为似乎在显示源时实现(单击右上角的源按钮)。如果不是从模式窗口打开,而是从 UI 或任何其他子窗口打开,行为也应该是相同的。

我尝试了几件事:

  • 无法使用 Popupview,因为我需要从组件(按钮或图像)打开窗口

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

  • 将 ClickListener 添加到 UI 并没有帮助,因为单击模式窗口时未触发该事件。

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

谢谢拉斐尔

4

6 回答 6

4

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

  1. @Steven Spungin 提出的聚焦/模糊方法确实有效,但前提是窗口内没有其他可聚焦元素。否则,单击其中一个元素将关闭窗口,这远非预期。
  2. @Steven Spungin 为他的第一个答案提出了一个使用“玻璃元素”的解决方案,但它仅适用于模态窗口,并且通常不是很灵活。
  3. @dwi wahyu utomo 的回答,他建议添加点击侦听器UI.getCurrent().addClickListener并检查点击事件坐标是否在窗口内,但它有一个主要问题:点击事件被 Vaadin “消耗”并且不会传播到浏览器。这意味着单击页面上的任何位置都不会导致正常行为,例如右键单击时不会显示本机上下文菜单。

我的想法是创建一个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);
于 2017-05-12T18:51:13.140 回答
3

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

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;
}
于 2014-09-26T20:20:00.870 回答
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);
  }
 }

 ...
}

希望能解决你的问题

于 2016-04-15T07:46:13.780 回答
2

尝试这个:

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

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

window.show();
window.focus();
于 2016-11-03T11:52:14.337 回答
0

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

 window.setModal(false); 

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

 window.addClickListener(new MouseEvents.ClickListener() {

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

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

于 2013-12-30T21:39:04.210 回答
0

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

在 Vaadin 中打开模态窗口后,UI 不会收到任何点击事件,但窗口会收到。但是,客户端不会向服务器发送玻璃点击。

您需要扩展 Window 以接收对玻璃的点击。单击玻璃后,您将关闭窗口。

注意:我们假设 Vaadin 如何创建 Window 元素以访问 Glass 元素。很可能有更好的方法。简而言之,我们等待窗口附加,然后等待 100 毫秒让玻璃附加,然后在玻璃上安装点击处理程序。

点击处理程序

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

扩展器 - 服务器端

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

扩展器 - 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();
                });
于 2017-03-09T15:54:35.267 回答