Vaadin 在具有活动编辑器的网格上双击按钮引发异常 ("java.lang.NullPointerException: Editor can't edit null")
Vaadin double clicking button on grid with active editor throws exception ("java.lang.NullPointerException: Editor can't edit null")
我有一个带有活动缓冲编辑器的 vaadin 网格。默认情况下,双击一行时会打开编辑器。一切正常,除了当我双击按钮时出现异常:(异常没有指向我的代码的任何地方)
java.lang.NullPointerException: Editor can't edit null
at java.base/java.util.Objects.requireNonNull(Objects.java:246)
at com.vaadin.ui.components.grid.EditorImpl.doEdit(EditorImpl.java:216)
at com.vaadin.ui.components.grid.EditorImpl.bind(EditorImpl.java:151)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:155)
at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:116)
at com.vaadin.server.communication.ServerRpcHandler.handleInvocation(ServerRpcHandler.java:445)
at com.vaadin.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:410)
at com.vaadin.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:274)
at com.vaadin.server.communication.PushHandler.lambda$new(PushHandler.java:145)
at com.vaadin.server.communication.PushHandler.callWithUi(PushHandler.java:235)
at com.vaadin.server.communication.PushHandler.onMessage(PushHandler.java:520)
at com.vaadin.server.communication.PushAtmosphereHandler.onMessage(PushAtmosphereHandler.java:87)
at com.vaadin.server.communication.PushAtmosphereHandler.onRequest(PushAtmosphereHandler.java:77)
at org.atmosphere.cpr.AsynchronousProcessor.action(AsynchronousProcessor.java:223)
at org.atmosphere.cpr.AsynchronousProcessor.suspended(AsynchronousProcessor.java:115)
at org.atmosphere.container.Servlet30CometSupport.service(Servlet30CometSupport.java:67)
at org.atmosphere.cpr.AtmosphereFramework.doCometSupport(AtmosphereFramework.java:2284)
at org.atmosphere.websocket.DefaultWebSocketProcessor.dispatch(DefaultWebSocketProcessor.java:593)
at org.atmosphere.websocket.DefaultWebSocketProcessor.run(DefaultWebSocketProcessor.java:345)
at org.atmosphere.util.VoidExecutorService.execute(VoidExecutorService.java:101)
at org.atmosphere.websocket.DefaultWebSocketProcessor.dispatch(DefaultWebSocketProcessor.java:340)
at org.atmosphere.websocket.DefaultWebSocketProcessor.invokeWebSocketProtocol(DefaultWebSocketProcessor.java:447)
at org.atmosphere.container.JSR356Endpoint.onMessage(JSR356Endpoint.java:272)
at org.atmosphere.container.JSR356Endpoint.onMessage(JSR356Endpoint.java:269)
at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:395)
at org.apache.tomcat.websocket.server.WsFrameServer.sendMessageText(WsFrameServer.java:119)
at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:495)
at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:294)
at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:133)
at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:82)
at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:171)
at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:151)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:148)
at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:54)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:53)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:844)
这是我将按钮添加到网格的方式:
// Adding the column
grid.addComponentColumn(this::buildAddButton);
这是returns每行按钮的方法:
// Building the button
private Button buildAddButton(ProductTemplate p) {
Button button = new Button("add");
// Configurate the button
...
return button;
}
不幸的是,我对这个问题知之甚少......
由于 vaadin 按钮不会停止点击事件传播,我尝试了这个:
通过从布局中移除点击侦听器,将按钮添加到布局以 "block" 点击事件。
// Adding the column
grid.addComponentColumn(this::buildAddLayout);
构建布局而不只是按钮:
// Building the layout with the button
private VerticalLayout buildAddLayout(ProductTemplate p) {
Button button = new Button("add");
// Configurate the button
...
VerticalLayout layout = new VerticalLayout();
layout.addComponent(button);
layout.getListeners(Event.class).clear();
return layout;
}
单击时禁用按钮,直到它的任务完成
完成(禁止双击按钮)。
// Building the button
private Button buildAddButton(ProductTemplate p) {
Button button = new Button("add");
// Configurate the button
...
button.addClickListener(e -> {
button.setEnabled(false);
buttonClicked(p);
button.setEnabled(true);
});
return button;
}
两者都没有让我能够摆脱异常。有什么建议吗?在启用编辑器的情况下双击网格上的按钮时如何防止此类异常?
编辑:
编辑器正在尝试编辑按钮的值,显然不可能。我想阻止编辑这样做。 ("can't edit null "表示 vaadin 无法通过按钮创建有效的 bean)
编辑 2:我之前在第一次编辑中做出的假设似乎是错误的。我的按钮点击刷新了网格,我无法阻止编辑器尝试编辑一行,即使所有项目都被删除并重新加载。
这里有一个class和一个pom,可以用来重现异常(点击按钮速度太快时出现异常):
MyUI.java
package com.example.sample;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.annotation.WebServlet;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Button;
import com.vaadin.ui.Grid;
import com.vaadin.ui.Notification;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
@Theme("mytheme")
public class MyUI extends UI {
List<Product> items = new ArrayList<Product>();
Grid<Product> grid;
@Override
protected void init(VaadinRequest vaadinRequest) {
items.add(new Product("test", "test test"));
final VerticalLayout layout = new VerticalLayout();
grid = new Grid<Product>();
layout.addComponent(grid);
grid.getEditor().setBuffered(true);
grid.getEditor().setEnabled(true);
grid.removeAllColumns();
grid.addComponentColumn(this::buildAddButton);
TextField nameField = new TextField();
TextField descriptionField = new TextField();
grid.addColumn(Product::getName).setCaption("Name").setEditorComponent(nameField, Product::setName)
.setExpandRatio(1);
grid.addColumn(Product::getDescription).setCaption("Description")
.setEditorComponent(descriptionField, Product::setDescription).setExpandRatio(1);
grid.getEditor().addSaveListener(event -> {
Notification.show((event.getBean() + "saved"));
});
grid.setItems(items);
setContent(layout);
}
private Button buildAddButton(Product p) {
Button button = new Button();
button.addClickListener(event -> addButtonClicked(p));
return button;
}
private void addButtonClicked(Product p) {
refreshGrid();
}
private void refreshGrid() {
grid.setItems(items);
}
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}
public class Product {
String name;
String description;
public Product(String name, String description) {
super();
this.name = name;
this.description = description;
}
public Product() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return name + " " + description;
}
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>sample</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>sample</name>
<prerequisites>
<maven>3</maven>
</prerequisites>
<properties>
<vaadin.version>8.3.1</vaadin.version>
<vaadin.plugin.version>8.3.1</vaadin.plugin.version>
<jetty.plugin.version>9.3.9.v20160517</jetty.plugin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<!-- If there are no local customizations, this can also be "fetch" or "cdn" -->
<vaadin.widgetset.mode>local</vaadin.widgetset.mode>
</properties>
<repositories>
<repository>
<id>vaadin-addons</id>
<url>http://maven.vaadin.com/vaadin-addons</url>
</repository>
</repositories>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-bom</artifactId>
<version>${vaadin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-server</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-push</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-client-compiled</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-themes</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<!-- Exclude an unnecessary file generated by the GWT compiler. -->
<packagingExcludes>WEB-INF/classes/VAADIN/widgetsets/WEB-INF/**</packagingExcludes>
</configuration>
</plugin>
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-maven-plugin</artifactId>
<version>${vaadin.plugin.version}</version>
<executions>
<execution>
<goals>
<goal>update-theme</goal>
<goal>update-widgetset</goal>
<goal>compile</goal>
<!-- Comment out compile-theme goal to use on-the-fly theme compilation -->
<goal>compile-theme</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.0.0</version>
<!-- Clean up also any pre-compiled themes -->
<configuration>
<filesets>
<fileset>
<directory>src/main/webapp/VAADIN/themes</directory>
<includes>
<include>**/styles.css</include>
<include>**/styles.scss.cache</include>
</includes>
</fileset>
</filesets>
</configuration>
</plugin>
<!-- The Jetty plugin allows us to easily test the development build by
running jetty:run on the command line. -->
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jetty.plugin.version}</version>
<configuration>
<scanIntervalSeconds>2</scanIntervalSeconds>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<!-- Vaadin pre-release repositories -->
<id>vaadin-prerelease</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<repositories>
<repository>
<id>vaadin-prereleases</id>
<url>http://maven.vaadin.com/vaadin-prereleases</url>
</repository>
<repository>
<id>vaadin-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>vaadin-prereleases</id>
<url>http://maven.vaadin.com/vaadin-prereleases</url>
</pluginRepository>
<pluginRepository>
<id>vaadin-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
</project>
免责声明:由于缺少setHandleWidgetEvents
方法,此解决方案不适用于8.3之前的版本。否则,虽然复杂,但此解决方案可能适用于某些人:
从按钮中删除单击事件处理 (addClickListener
)。那里无法获得有关双击的信息,BUT
有一种方法可以使用 addItemClickListener
及其 clickedItem.getMouseEventDetails().isDoubleClick()
,但是 再次
使用 grid.getEditor().setEnabled(true);
启用网格后,双击检查总是 returns false
,所以
默认情况下您需要禁用编辑器grid.getEditor().setEnabled(false);
(这里有一个很好的相关答案Couldn't capture double click event using vaadin 7)
并且,相反,一旦双击事件发生(并打开一行)就重新启用它
grid.getEditor().setEnabled(true);
grid.getEditor().editRow(item.getRowIndex());
如果按钮列被点击一次,执行你的addButtonClicked
操作然后
- 为了从网格列的组件接收事件,单击按钮时,列应该能够处理事件。这是使用
grid.addComponentColumn(this::buildAddButton).setHandleWidgetEvents(true).setId("buttonClick");
实现的
init
方法的完整修改代码。所有其他保持不变(另外,请记住从按钮中删除 addClickListener
):
items.add(new Product("test", "test test"));
grid = new Grid<Product>();
grid.getEditor().setBuffered(true);
grid.getEditor().setEnabled(false);
grid.removeAllColumns();
// Important! Propagate events from components to Grid
grid.addComponentColumn(this::buildAddButton).setHandleWidgetEvents(true).setId("buttonClick");
TextField nameField = new TextField();
TextField descriptionField = new TextField();
grid.addColumn(Product::getName).setCaption("Name").setEditorComponent(nameField, Product::setName)
.setExpandRatio(1);
grid.addColumn(Product::getDescription).setCaption("Description")
.setEditorComponent(descriptionField, Product::setDescription).setExpandRatio(1);
//Once close editor--> Disable it
grid.getEditor().addSaveListener(event -> {
grid.getEditor().setEnabled(false);
});
grid.getEditor().addCancelListener(e->{
grid.getEditor().setEnabled(false);
});
//THIS IS WHERE ALL THE LOGIC IS HAPPENING
grid.addItemClickListener(item->{
//If the button column is clicked
if("buttonClick".equals(item.getColumn().getId())){
//Regual click--> update content; also fired twice before editor is opened
if(!item.getMouseEventDetails().isDoubleClick()){
addButtonClicked(item.getItem());
}
//If Double click is detected, just opened editor. The data is already updated
else{
grid.getEditor().setEnabled(true);
grid.getEditor().editRow(item.getRowIndex());
}
}
//In all the other cases, when double click is detected--> open editor
else if(item.getMouseEventDetails().isDoubleClick()){
grid.getEditor().setEnabled(true);
grid.getEditor().editRow(item.getRowIndex());
}
});
grid.setItems(items);
addComponent(grid);
我有一个带有活动缓冲编辑器的 vaadin 网格。默认情况下,双击一行时会打开编辑器。一切正常,除了当我双击按钮时出现异常:(异常没有指向我的代码的任何地方)
java.lang.NullPointerException: Editor can't edit null
at java.base/java.util.Objects.requireNonNull(Objects.java:246)
at com.vaadin.ui.components.grid.EditorImpl.doEdit(EditorImpl.java:216)
at com.vaadin.ui.components.grid.EditorImpl.bind(EditorImpl.java:151)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:155)
at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:116)
at com.vaadin.server.communication.ServerRpcHandler.handleInvocation(ServerRpcHandler.java:445)
at com.vaadin.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:410)
at com.vaadin.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:274)
at com.vaadin.server.communication.PushHandler.lambda$new(PushHandler.java:145)
at com.vaadin.server.communication.PushHandler.callWithUi(PushHandler.java:235)
at com.vaadin.server.communication.PushHandler.onMessage(PushHandler.java:520)
at com.vaadin.server.communication.PushAtmosphereHandler.onMessage(PushAtmosphereHandler.java:87)
at com.vaadin.server.communication.PushAtmosphereHandler.onRequest(PushAtmosphereHandler.java:77)
at org.atmosphere.cpr.AsynchronousProcessor.action(AsynchronousProcessor.java:223)
at org.atmosphere.cpr.AsynchronousProcessor.suspended(AsynchronousProcessor.java:115)
at org.atmosphere.container.Servlet30CometSupport.service(Servlet30CometSupport.java:67)
at org.atmosphere.cpr.AtmosphereFramework.doCometSupport(AtmosphereFramework.java:2284)
at org.atmosphere.websocket.DefaultWebSocketProcessor.dispatch(DefaultWebSocketProcessor.java:593)
at org.atmosphere.websocket.DefaultWebSocketProcessor.run(DefaultWebSocketProcessor.java:345)
at org.atmosphere.util.VoidExecutorService.execute(VoidExecutorService.java:101)
at org.atmosphere.websocket.DefaultWebSocketProcessor.dispatch(DefaultWebSocketProcessor.java:340)
at org.atmosphere.websocket.DefaultWebSocketProcessor.invokeWebSocketProtocol(DefaultWebSocketProcessor.java:447)
at org.atmosphere.container.JSR356Endpoint.onMessage(JSR356Endpoint.java:272)
at org.atmosphere.container.JSR356Endpoint.onMessage(JSR356Endpoint.java:269)
at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:395)
at org.apache.tomcat.websocket.server.WsFrameServer.sendMessageText(WsFrameServer.java:119)
at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:495)
at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:294)
at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:133)
at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:82)
at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:171)
at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:151)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:148)
at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:54)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:53)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:844)
这是我将按钮添加到网格的方式:
// Adding the column
grid.addComponentColumn(this::buildAddButton);
这是returns每行按钮的方法:
// Building the button
private Button buildAddButton(ProductTemplate p) {
Button button = new Button("add");
// Configurate the button
...
return button;
}
不幸的是,我对这个问题知之甚少...... 由于 vaadin 按钮不会停止点击事件传播,我尝试了这个:
通过从布局中移除点击侦听器,将按钮添加到布局以 "block" 点击事件。
// Adding the column grid.addComponentColumn(this::buildAddLayout);
构建布局而不只是按钮:
// Building the layout with the button private VerticalLayout buildAddLayout(ProductTemplate p) { Button button = new Button("add"); // Configurate the button ... VerticalLayout layout = new VerticalLayout(); layout.addComponent(button); layout.getListeners(Event.class).clear(); return layout; }
单击时禁用按钮,直到它的任务完成 完成(禁止双击按钮)。
// Building the button private Button buildAddButton(ProductTemplate p) { Button button = new Button("add"); // Configurate the button ... button.addClickListener(e -> { button.setEnabled(false); buttonClicked(p); button.setEnabled(true); }); return button; }
两者都没有让我能够摆脱异常。有什么建议吗?在启用编辑器的情况下双击网格上的按钮时如何防止此类异常?
编辑:
编辑器正在尝试编辑按钮的值,显然不可能。我想阻止编辑这样做。 ("can't edit null "表示 vaadin 无法通过按钮创建有效的 bean)
编辑 2:我之前在第一次编辑中做出的假设似乎是错误的。我的按钮点击刷新了网格,我无法阻止编辑器尝试编辑一行,即使所有项目都被删除并重新加载。
这里有一个class和一个pom,可以用来重现异常(点击按钮速度太快时出现异常):
MyUI.java
package com.example.sample;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.annotation.WebServlet;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Button;
import com.vaadin.ui.Grid;
import com.vaadin.ui.Notification;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
@Theme("mytheme")
public class MyUI extends UI {
List<Product> items = new ArrayList<Product>();
Grid<Product> grid;
@Override
protected void init(VaadinRequest vaadinRequest) {
items.add(new Product("test", "test test"));
final VerticalLayout layout = new VerticalLayout();
grid = new Grid<Product>();
layout.addComponent(grid);
grid.getEditor().setBuffered(true);
grid.getEditor().setEnabled(true);
grid.removeAllColumns();
grid.addComponentColumn(this::buildAddButton);
TextField nameField = new TextField();
TextField descriptionField = new TextField();
grid.addColumn(Product::getName).setCaption("Name").setEditorComponent(nameField, Product::setName)
.setExpandRatio(1);
grid.addColumn(Product::getDescription).setCaption("Description")
.setEditorComponent(descriptionField, Product::setDescription).setExpandRatio(1);
grid.getEditor().addSaveListener(event -> {
Notification.show((event.getBean() + "saved"));
});
grid.setItems(items);
setContent(layout);
}
private Button buildAddButton(Product p) {
Button button = new Button();
button.addClickListener(event -> addButtonClicked(p));
return button;
}
private void addButtonClicked(Product p) {
refreshGrid();
}
private void refreshGrid() {
grid.setItems(items);
}
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}
public class Product {
String name;
String description;
public Product(String name, String description) {
super();
this.name = name;
this.description = description;
}
public Product() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return name + " " + description;
}
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>sample</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>sample</name>
<prerequisites>
<maven>3</maven>
</prerequisites>
<properties>
<vaadin.version>8.3.1</vaadin.version>
<vaadin.plugin.version>8.3.1</vaadin.plugin.version>
<jetty.plugin.version>9.3.9.v20160517</jetty.plugin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<!-- If there are no local customizations, this can also be "fetch" or "cdn" -->
<vaadin.widgetset.mode>local</vaadin.widgetset.mode>
</properties>
<repositories>
<repository>
<id>vaadin-addons</id>
<url>http://maven.vaadin.com/vaadin-addons</url>
</repository>
</repositories>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-bom</artifactId>
<version>${vaadin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-server</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-push</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-client-compiled</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-themes</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<!-- Exclude an unnecessary file generated by the GWT compiler. -->
<packagingExcludes>WEB-INF/classes/VAADIN/widgetsets/WEB-INF/**</packagingExcludes>
</configuration>
</plugin>
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-maven-plugin</artifactId>
<version>${vaadin.plugin.version}</version>
<executions>
<execution>
<goals>
<goal>update-theme</goal>
<goal>update-widgetset</goal>
<goal>compile</goal>
<!-- Comment out compile-theme goal to use on-the-fly theme compilation -->
<goal>compile-theme</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.0.0</version>
<!-- Clean up also any pre-compiled themes -->
<configuration>
<filesets>
<fileset>
<directory>src/main/webapp/VAADIN/themes</directory>
<includes>
<include>**/styles.css</include>
<include>**/styles.scss.cache</include>
</includes>
</fileset>
</filesets>
</configuration>
</plugin>
<!-- The Jetty plugin allows us to easily test the development build by
running jetty:run on the command line. -->
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jetty.plugin.version}</version>
<configuration>
<scanIntervalSeconds>2</scanIntervalSeconds>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<!-- Vaadin pre-release repositories -->
<id>vaadin-prerelease</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<repositories>
<repository>
<id>vaadin-prereleases</id>
<url>http://maven.vaadin.com/vaadin-prereleases</url>
</repository>
<repository>
<id>vaadin-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>vaadin-prereleases</id>
<url>http://maven.vaadin.com/vaadin-prereleases</url>
</pluginRepository>
<pluginRepository>
<id>vaadin-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
</project>
免责声明:由于缺少setHandleWidgetEvents
方法,此解决方案不适用于8.3之前的版本。否则,虽然复杂,但此解决方案可能适用于某些人:
从按钮中删除单击事件处理 (
addClickListener
)。那里无法获得有关双击的信息,BUT有一种方法可以使用
addItemClickListener
及其clickedItem.getMouseEventDetails().isDoubleClick()
,但是 再次使用
grid.getEditor().setEnabled(true);
启用网格后,双击检查总是 returnsfalse
,所以默认情况下您需要禁用编辑器
grid.getEditor().setEnabled(false);
(这里有一个很好的相关答案Couldn't capture double click event using vaadin 7)并且,相反,一旦双击事件发生(并打开一行)就重新启用它
grid.getEditor().setEnabled(true); grid.getEditor().editRow(item.getRowIndex());
如果按钮列被点击一次,执行你的
addButtonClicked
操作然后- 为了从网格列的组件接收事件,单击按钮时,列应该能够处理事件。这是使用
grid.addComponentColumn(this::buildAddButton).setHandleWidgetEvents(true).setId("buttonClick");
实现的
init
方法的完整修改代码。所有其他保持不变(另外,请记住从按钮中删除 addClickListener
):
items.add(new Product("test", "test test"));
grid = new Grid<Product>();
grid.getEditor().setBuffered(true);
grid.getEditor().setEnabled(false);
grid.removeAllColumns();
// Important! Propagate events from components to Grid
grid.addComponentColumn(this::buildAddButton).setHandleWidgetEvents(true).setId("buttonClick");
TextField nameField = new TextField();
TextField descriptionField = new TextField();
grid.addColumn(Product::getName).setCaption("Name").setEditorComponent(nameField, Product::setName)
.setExpandRatio(1);
grid.addColumn(Product::getDescription).setCaption("Description")
.setEditorComponent(descriptionField, Product::setDescription).setExpandRatio(1);
//Once close editor--> Disable it
grid.getEditor().addSaveListener(event -> {
grid.getEditor().setEnabled(false);
});
grid.getEditor().addCancelListener(e->{
grid.getEditor().setEnabled(false);
});
//THIS IS WHERE ALL THE LOGIC IS HAPPENING
grid.addItemClickListener(item->{
//If the button column is clicked
if("buttonClick".equals(item.getColumn().getId())){
//Regual click--> update content; also fired twice before editor is opened
if(!item.getMouseEventDetails().isDoubleClick()){
addButtonClicked(item.getItem());
}
//If Double click is detected, just opened editor. The data is already updated
else{
grid.getEditor().setEnabled(true);
grid.getEditor().editRow(item.getRowIndex());
}
}
//In all the other cases, when double click is detected--> open editor
else if(item.getMouseEventDetails().isDoubleClick()){
grid.getEditor().setEnabled(true);
grid.getEditor().editRow(item.getRowIndex());
}
});
grid.setItems(items);
addComponent(grid);