【发布时间】:2010-10-29 22:02:37
【问题描述】:
有没有一种非 JSNI 方法可以在 DialogBox 的标题栏区域添加关闭按钮?
【问题讨论】:
标签: gwt
有没有一种非 JSNI 方法可以在 DialogBox 的标题栏区域添加关闭按钮?
【问题讨论】:
标签: gwt
我们从项目一开始就使用 GWT-ext。这是个坏主意。他们有很多很酷的小部件,但它们不是 GWT 小部件,而且它们与 GWT 小部件不兼容。一旦选择了 GWT-Ext,一切,甚至是事件机制,都必须采用 GWT-Ext 方式,而不是 GWT 方式。这个库不会为最新版本的 GWT 更新,因为 javascript 库 Ext 不再免费。我们现在正在从我们的项目中删除 GWT-Ext。
无法在 GWT DialogBox 标题中添加不同的小部件,但您可以扩展“DecoratedPanel”(它是 DialogBox 父级)。查看 DialogBox 源代码以了解技术,特别是如何将 Caption 对象添加到面板以及如何实现窗口拖动。
这就是我们在这里所做的,而且效果很好。我们已经创建了自己的 Caption 类,它扩展了 FocusablePanel(一个捕获所有鼠标事件的 SimplePanel),并且我们向它添加了一个带有按钮和文本的 HorizontalPanel。我们必须通过调用 super 方法来覆盖 onAttach() 和 onDetach()(它们是受保护的)。
我相信我不能把我们的源代码放在这里,所以我只能给你这些提示。
【讨论】:
您可以通过在对话框的中心面板上添加一个按钮来实现:
Image closeButton = new Image("");
closeButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
registerBox.hide();
}
});
closeButton.setStyleName("TopRight");
然后用 CSS 定位它:
.TopRight {
float:right;
margin-top:-22px;
width:16px;
height:16px;
display:block;
background-image: url(images/cancel_16.png);
}
【讨论】:
我创建了这个字幕类:
public class DialogBoxCaptionWithCancel extends Composite
implements Caption, HasClickHandlers {
@UiField
HTMLPanel mainPanel;
@UiField
HTML captionLabel;
@UiField
PushButton cancelButton;
private HandlerManager handlerManager = null;
private static final Binder binder = GWT.create(Binder.class);
interface Binder extends UiBinder<Widget, DialogBoxCaptionWithCancel> {
}
public DialogBoxCaptionWithCancel() {
initWidget(binder.createAndBindUi(this));
mainPanel.setStyleName("Caption");
Image upImage = new Image("images/closeWindow.png");
Image hoverImage = new Image("images/closeWindowFocus.png");
cancelButton.getUpFace().setImage(upImage);
cancelButton.getUpHoveringFace().setImage(hoverImage);
cancelButton.setStylePrimaryName("none");
}
/*
* (non-Javadoc)
*
* @see com.google.gwt.user.client.ui.Widget#onLoad()
*/
@Override
protected void onLoad() {
super.onLoad();
handlerManager = new HandlerManager(this);
}
@UiHandler("cancelButton")
public void cancelButtonOnClick(ClickEvent event) {
handlerManager.fireEvent(event);
}
@Override
public HandlerRegistration addMouseDownHandler(MouseDownHandler handler) {
return handlerManager.addHandler(MouseDownEvent.getType(), handler);
}
@Override
public HandlerRegistration addMouseUpHandler(MouseUpHandler handler) {
return handlerManager.addHandler(MouseUpEvent.getType(), handler);
}
@Override
public HandlerRegistration addMouseOutHandler(MouseOutHandler handler) {
return handlerManager.addHandler(MouseOutEvent.getType(), handler);
}
@Override
public HandlerRegistration addMouseOverHandler(MouseOverHandler handler) {
return handlerManager.addHandler(MouseOverEvent.getType(), handler);
}
@Override
public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
return handlerManager.addHandler(MouseMoveEvent.getType(), handler);
}
@Override
public HandlerRegistration addMouseWheelHandler(MouseWheelHandler handler) {
return handlerManager.addHandler(MouseWheelEvent.getType(), handler);
}
@Override
public String getHTML() {
return "";
}
@Override
public void setHTML(String html) {
}
@Override
public String getText() {
return this.captionLabel.getText();
}
@Override
public void setText(String text) {
this.captionLabel.setText(text);
}
@Override
public void setHTML(SafeHtml html) {
}
@Override
public HandlerRegistration addClickHandler(ClickHandler handler) {
return handlerManager.addHandler(ClickEvent.getType(), handler);
}
}
当您将鼠标悬停在取消按钮上时,图像只是从 IE8 的行为中捕获的。
UiBinder 代码如下:
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder
xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
<ui:style>
.htmlField {
width: 100%;
}
.pushButton {
border: none;
padding: 0px;
width: 49px;
height: 21px;
}
</ui:style>
<g:HTMLPanel ui:field="mainPanel">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td width="100%">
<g:HTML ui:field="captionLabel" addStyleNames="{style.htmlField}"></g:HTML>
</td>
<td>
<g:PushButton ui:field="cancelButton" addStyleNames="{style.pushButton}"></g:PushButton>
</td>
</tr>
</table>
</g:HTMLPanel>
</ui:UiBinder>
那么我扩展 DialogBox 的类具有以下内容:
public class MyDialogBox extends DialogBox implements ClickHandler {
...
// instantiate the caption with the cancel button
private static DialogBoxCaptionWithCancel caption = new DialogBoxCaptionWithCancel();
...
public MyDialogBox() {
// construct the dialog box with the custom caption
super(false, false, caption);
setWidget(binder.createAndBindUi(this));
// set the caption's text
caption.setText("My Caption");
}
....
protected void onLoad() {
super.onLoad();
// let us react to the captions cancel button
caption.addClickHandler(this);
}
...
@Override
public void onClick(ClickEvent event) {
// the caption's cancel button was clicked
this.hide();
}
【讨论】:
更简单的解决方案是使用 gwt-ext (http://code.google.com/p/gwt-ext/)。它是免费的,易于使用和集成。 你可以看到他们的展示http://www.gwt-ext.com/demo/。 我认为您想要的是 MessageBox 或 Layout Window(它们位于展示的 Windows 类别中)。
问候。
【讨论】:
你可以试试这个,由 fungus1487 稍微改进的解决方案:
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.i18n.client.HasDirection;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.*;
/**
* @author Andrey Talnikov
*/
public class ClosablePopup extends DialogBox {
private Anchor closeAnchor;
/**
* Instantiates new closable popup.
*
* @param title the title
* @param defaultClose it {@code true}, hide popup on 'x' click
*/
public ClosablePopup(String title, boolean defaultClose) {
super(true);
closeAnchor = new Anchor("x");
FlexTable captionLayoutTable = new FlexTable();
captionLayoutTable.setWidth("100%");
captionLayoutTable.setText(0, 0, title);
captionLayoutTable.setWidget(0, 1, closeAnchor);
captionLayoutTable.getCellFormatter().setHorizontalAlignment(0, 1,
HasHorizontalAlignment.HorizontalAlignmentConstant.endOf(HasDirection.Direction.LTR));
HTML caption = (HTML) getCaption();
caption.getElement().appendChild(captionLayoutTable.getElement());
caption.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
EventTarget target = event.getNativeEvent().getEventTarget();
Element targetElement = (Element) target.cast();
if (targetElement == closeAnchor.getElement()) {
closeAnchor.fireEvent(event);
}
}
});
if (defaultClose) {
addCloseHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
hide();
}
});
}
}
public void addCloseHandler(ClickHandler handler) {
closeAnchor.addClickHandler(handler);
}
}
【讨论】:
是的有
不,没有——至少在不摆弄 GWT 的 DialogBox 类本身或使用通用小部件重新创建 DialogBox 的情况下是这样。这是 GWT 中的一个已知问题,又名 issue 1405(加星标表示您的兴趣)。
但是; DialogBox 没有为我们提供执行此操作的工具,因此我们需要对其进行扩展 - 编辑: 这不起作用。
如果您想直接替换 DialogBox,您可以将您的类命名为 DialogBox 并导入它,而不是 GWT 中包含的那个。 This thread 在 GWT 论坛上提供了有关如何完成此操作的更详细信息(已过时,使用侦听器) 已过时,DialogBox 的内部结构自此线程以来发生了很大变化 - 它不起作用。
这是我为获得相同结果而编写的一些代码(使用链接线程作为指导)。这不起作用:
我的对话框:
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
public class MyDialogBox extends DialogBox {
private class crossHandler implements ClickHandler, MouseOverHandler, MouseOutHandler
{
@Override
public void onClick(ClickEvent event) {
hide();
Window.alert("Click!");
}
@Override
public void onMouseOver(MouseOverEvent event) {
DOM.setStyleAttribute(cross.getElement(), "font-weight", "bold");
}
@Override
public void onMouseOut(MouseOutEvent event) {
DOM.setStyleAttribute(cross.getElement(), "font-weight", "normal");
}
}
Label cross = new Label("X"); // The close button
crossHandler crosshandler = new crossHandler();
HTML caption = new HTML(); // The caption aka title
HorizontalPanel captionPanel = new HorizontalPanel(); // Contains caption and cross
/**
* Creates an empty dialog box. It should not be shown until its child widget
* has been added using {@link #add(Widget)}.
*/
public MyDialogBox()
{
this(false);
}
/**
* Creates an empty dialog box specifying its "auto-hide" property. It should
* not be shown until its child widget has been added using
* {@link #add(Widget)}.
*
* @param autoHide <code>true</code> if the dialog should be automatically
* hidden when the user clicks outside of it
*/
public MyDialogBox(boolean autoHide) {
this(autoHide, true);
}
/**
* Creates an empty dialog box specifying its "auto-hide" property. It should
* not be shown until its child widget has been added using
* {@link #add(Widget)}.
*
* @param autoHide <code>true</code> if the dialog should be automatically
* hidden when the user clicks outside of it
* @param modal <code>true</code> if keyboard and mouse events for widgets not
* contained by the dialog should be ignored
*/
public MyDialogBox(boolean autoHide, boolean modal)
{
super(autoHide, modal);
cross.addClickHandler(crosshandler);
cross.addMouseOutHandler(crosshandler);
cross.addMouseOverHandler(crosshandler);
captionPanel.add(caption);
captionPanel.add(cross);
captionPanel.setStyleName("caption");
Element td = getCellElement(0, 1); // Get the cell element that holds the caption
td.setInnerHTML(""); // Remove the old caption
td.appendChild(captionPanel.getElement());
}
@Override
public void setText(String text)
{
caption.setText(text);
}
public String getText()
{
return caption.getText();
}
public void setHtml(String html)
{
caption.setHTML(html);
}
public String getHtml()
{
return caption.getHTML();
}
注意:此代码不起作用。 ClickEvent 不是从cross 发送的,而是从MyDialogBox 发送的,无论您是否将ClickHandlers 添加到cross,IOW MyDialogBox 是发送者/源,因此无法检查cross。当点击十字时,由于某些原因,它不会触发 ClickEvent。
编辑: 除非您从头开始(几乎)编写自己的 DialogBox 或修复问题 1405,否则这似乎无法在没有 hack 的情况下完成。当然有许多现有的库已经解决了这个问题,即 SmartGWT 和 GWT-Ext,但是他们的实现大多是从头开始的。
所以一句话回答你的问题:是的,有办法,但你不会喜欢它:)
【讨论】:
我想一个简单的答案是实例化一个小部件来替换 DialogBox 中的标准 Caption 小部件。 我创建了一个右侧有一个按钮的标题,您可以选择对它的引用。 然后你可以添加任何你想要的点击事件。
在 GWT 2.4 中,我使用了以下解决方案:
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.dom.client.MouseWheelHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.DialogBox.Caption;
/**
* @author Cristiano Sumariva
*/
public class ButtonCaption extends HorizontalPanel implements Caption
{
protected InlineLabel text;
protected PushButton closeDialog;
/**
* @return the button at caption
*/
public PushButton getCloseButton()
{
return closeDialog;
}
public ButtonCaption( String label )
{
super();
setWidth( "100%" );
setStyleName( "Caption" ); // so you have same styling as standard caption widget
closeDialog = new PushButton();
add( text = new InlineLabel( label ) );
add( closeDialog );
setCellWidth( closeDialog, "1px" ); // to make button cell minimal enough to it
}
/* (non-Javadoc)
* @see com.google.gwt.event.dom.client.HasMouseDownHandlers#addMouseDownHandler(com.google.gwt.event.dom.client.MouseDownHandler)
*/
@Override
public HandlerRegistration addMouseDownHandler( MouseDownHandler handler )
{
return addMouseDownHandler( handler );
}
/* (non-Javadoc)
* @see com.google.gwt.event.dom.client.HasMouseUpHandlers#addMouseUpHandler(com.google.gwt.event.dom.client.MouseUpHandler)
*/
@Override
public HandlerRegistration addMouseUpHandler( MouseUpHandler handler )
{
return addMouseUpHandler( handler );
}
/* (non-Javadoc)
* @see com.google.gwt.event.dom.client.HasMouseOutHandlers#addMouseOutHandler(com.google.gwt.event.dom.client.MouseOutHandler)
*/
@Override
public HandlerRegistration addMouseOutHandler( MouseOutHandler handler )
{
return addMouseOutHandler( handler );
}
/* (non-Javadoc)
* @see com.google.gwt.event.dom.client.HasMouseOverHandlers#addMouseOverHandler(com.google.gwt.event.dom.client.MouseOverHandler)
*/
@Override
public HandlerRegistration addMouseOverHandler( MouseOverHandler handler )
{
return addMouseOverHandler( handler );
}
/* (non-Javadoc)
* @see com.google.gwt.event.dom.client.HasMouseMoveHandlers#addMouseMoveHandler(com.google.gwt.event.dom.client.MouseMoveHandler)
*/
@Override
public HandlerRegistration addMouseMoveHandler( MouseMoveHandler handler )
{
return addMouseMoveHandler( handler );
}
/* (non-Javadoc)
* @see com.google.gwt.event.dom.client.HasMouseWheelHandlers#addMouseWheelHandler(com.google.gwt.event.dom.client.MouseWheelHandler)
*/
@Override
public HandlerRegistration addMouseWheelHandler( MouseWheelHandler handler )
{
return addMouseWheelHandler( handler );
}
/* (non-Javadoc)
* @see com.google.gwt.user.client.ui.HasHTML#getHTML()
*/
@Override
public String getHTML()
{
return getElement().getInnerHTML();
}
/* (non-Javadoc)
* @see com.google.gwt.user.client.ui.HasHTML#setHTML(java.lang.String)
*/
@Override
public void setHTML( String html )
{
remove( text );
insert( text, 1 );
}
/* (non-Javadoc)
* @see com.google.gwt.user.client.ui.HasText#getText()
*/
@Override
public String getText()
{
return text.getText();
}
/* (non-Javadoc)
* @see com.google.gwt.user.client.ui.HasText#setText(java.lang.String)
*/
@Override
public void setText( String text )
{
this.text.setText( text );
}
/* (non-Javadoc)
* @see com.google.gwt.safehtml.client.HasSafeHtml#setHTML(com.google.gwt.safehtml.shared.SafeHtml)
*/
@Override
public void setHTML( SafeHtml html )
{
setHTML( html.asString() );
}
}
扩展 DialogBox 以使用可用的新 ButtonCaption
class CaptionCloseableDialogBox extends DialogBox
{
public CaptionCloseableDialogBox()
{
super( new ButtonCaption( "dialog box title" ) );
setAutoHideEnabled( false );
ButtonCaption ref = (ButtonCaption) this.getCaption();
PushButton closeButton = ref.getCloseButton();
// apply button face here closeButton;
closeButton.addClickHandler( /* attach any click handler here like close this dialog */ );
}
}
希望对大家有帮助。
【讨论】:
查看活动项目: http://code.google.com/p/gwt-mosaic/
正如他们的页面所述,他们的崇高目标是:
目标是通过使 API 尽可能接近 GWT 的标准小部件 API 来提供完整的小部件集。
已被困在 GXT 漩涡中。根本不喜欢他们如何要求用户为侦听器使用完全不同的 API 等。就他们而言,这是有道理的。毕竟,GXT 只是他们现有 javascript 库的一个端口。但是我一直在寻找这个 MOSAIC 项目太久了……
【讨论】:
只需使用 GWT 并且没有外部库,您就可以拦截标题元素上的点击事件并执行命中测试,以查看 x,y 鼠标坐标是否在锚元素(或您用作的其他元素)的范围内ClickHandler)。
// Create anchor we want to accept click events
final Anchor myAnchor = new Anchor("My Anchor");
// Add handler to anchor
myAnchor.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
Window.alert("Anchor was clicked");
}
});
// Create dialog
final DialogBox myDialog = new DialogBox();
myDialog.setText("My Dialog");
// Get caption element
final HTML caption = ((HTML)myDialog.getCaption());
// Add anchor to caption
caption.getElement().appendChild(myAnchor.getElement());
// Add click handler to caption
caption.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
// Get x,y caption click relative to the anchor
final int x = event.getRelativeX(myAnchor.getElement());
final int y = event.getRelativeY(myAnchor.getElement());
// Check click was within bounds of anchor
if(x >= 0 && y >= 0 &&
x <= myAnchor.getOffsetWidth() &&
y <= myAnchor.getOffsetHeight()) {
// Raise event on anchor
myAnchor.fireEvent(event);
}
}
});
// Show the dialog
myDialog.show();
【讨论】:
我意识到这已经过时了,但是您可以使用绝对定位和 0 的顶部和右侧来获得右上角的小部件。对话框本身是绝对定位的,因此您的小部件的定位将与它相反。
【讨论】:
如果您只是不想为所提出的问题提供简单的解决方案,则此方法可行:
Image button = new Image("images/cancel.png");
button.addClickHandler(new ClickHandler(){
public void onClick(ClickEvent event) {
hide();
}
});
button.setStyleName("dialog-close");
HorizontalPanel header = new HorizontalPanel();
header.add(new HTML("Example Tool"));
header.add(button);
setHTML(header.getElement().getInnerHTML());
【讨论】:
您可以在项目 synthfuljava 下的 google 代码中找到可关闭的对话框。 它实际上称为可滚动对话框,在标题处带有一个关闭 X 按钮。
以下博客解释了必须克服的障碍才能使标题 X 按钮能够侦听单击事件以使其工作:
http://h2g2java.blessedgeek.com/2009/07/gwt-useable-closeable-scrollable.html
【讨论】:
我认为cavila 的ButtonCaption 是最好的解决方案,但是在caption 的实现上有一个bug。调用其中一个被覆盖的方法会导致不定式循环,因为该方法会递归调用自身。
为了防止这种情况,您可以调用 InlineLabel text 上的方法:
@Override
public HandlerRegistration addMouseDownHandler( MouseDownHandler handler ) {
return text.addMouseDownHandler( handler );
}
【讨论】:
GWT 对话框的顶级 DIV 具有绝对定位,因此您可以使用关闭按钮执行相同操作。就 DOM 而言,这允许您将其放在对话框的主体中,但使其实际出现在标题中。
在下面的示例中,我将其放置在对话框的右上方,并使用填充将其置于标题的中心。
<ui:style>
.close {
position: absolute;
top: 0;
right: 0;
padding: 3px 3px 1px 3px !important;
border-radius: 4px;
margin: 5px;
}
</ui:style>
<g:PushButton ui:field="closeButton" addStyleNames="{style.close}">
<g:upFace image='{closeIcon}'/>
<g:downFace image='{closeIcon}'/>
<g:upHoveringFace image='{closeIcon}'/>
<g:downHoveringFace image='{closeIcon}'/>
<g:upDisabledFace image='{closeIcon}'/>
<g:downDisabledFace image='{closeIcon}'/>
</g:PushButton>
【讨论】: