我有一个带有主动缓冲编辑器的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$1.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$1(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$3.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$3.onMessage(JSR356Endpoint.java:272)
at org.atmosphere.container.JSR356Endpoint$3.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)
这就是我将Button添加到网格的方式:
// Adding the column
grid.addComponentColumn(this::buildAddButton);
这是返回每行按钮的方法:
// Building the button
private Button buildAddButton(ProductTemplate p) {
Button button = new Button("add");
// Configurate the button
...
return button;
}
不幸的是,我发现这个问题很少...... 由于vaadin按钮不会停止点击事件传播,我试过这个:
通过从布局中删除单击侦听器,将按钮添加到布局以“阻止”单击事件。
// 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;
}
两者都没能让我摆脱异常。任何建议,如何在启用编辑器双击网格上的按钮时如何防止此类异常?
编辑:
编辑器正在尝试编辑按钮的值,这显然是不可能的。我想阻止编辑这样做。 (“无法编辑null”表示vaadin无法从按钮中创建有效的bean)
编辑2:我之前在第一次编辑中做出的假设似乎是错误的。我的按钮单击是刷新网格,我无法阻止编辑器尝试编辑行,即使所有项目都被删除并重新加载。
这是一个类和一个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>
答案 0 :(得分:1)
免责声明:由于缺少setHandleWidgetEvents
方法,此解决方案不适用于8.3之前的版本。否则,虽然很复杂,但此解决方案可能对某人有效:
从按钮(addClickListener
)中删除单击事件处理。无法获取有关双击的信息,但是
有一种方法可以使用addItemClickListener
及其clickedItem.getMouseEventDetails().isDoubleClick()
,但是 再次
使用grid.getEditor().setEnabled(true);
启用网格后,双击检查总是返回false
,所以
默认情况下,您需要禁用编辑器grid.getEditor().setEnabled(false);
(这里是一个很好的相关答案Couldn't capture double click event using vaadin 7)
然后,而是在发生双击事件(并打开一行)后
grid.getEditor().setEnabled(true);
grid.getEditor().editRow(item.getRowIndex());
否则,如果单击一次 button 列,则执行您的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);