我有一个画布,可以在Vaadin中的画布上绘制边界框,并且我正在尝试实现一个撤消功能,以便最后一个绘制的框都将从数组和画布上移除。在undoLast方法(可在Canvas.java中找到并在MainLayout.java中调用)中,我删除了数组的最后一个元素(包含绘制的所有框的arrayBoxes),并尝试在重新绘制画布之前清除整个画布。留在数组中。但是,我收到一个数组错误。
MainLayout:
package com.vaadin.starter.beveragebuddy.backend;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dependency.HtmlImport;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.html.NativeButton;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.theme.Theme;
import com.vaadin.flow.theme.lumo.Lumo;
import com.vaadin.starter.beveragebuddy.ui.components.BoundingBox;
import com.vaadin.starter.beveragebuddy.ui.components.Canvas;
import com.vaadin.starter.beveragebuddy.ui.components.CanvasRenderingContext2D;
import com.vaadin.starter.beveragebuddy.ui.components.MousePosition;
import com.vaadin.flow.component.textfield.TextField;
import java.util.ArrayList;
/**
* The main layout contains the header with the navigation buttons, and the
* child views below that.
*/
@HtmlImport("frontend://styles/shared-styles.html")
@Route("")
@Theme(Lumo.class)
public class MainLayout extends VerticalLayout {
private CanvasRenderingContext2D ctx;
private Canvas canvas;
ArrayList<MousePosition> mousePosArray = Canvas.getMousePosArray();
ArrayList<BoundingBox> bb = Canvas.getArrayBoxes();
public static int count = 0;
public MainLayout() {
VerticalLayout footerLayout = new VerticalLayout();
H2 title = new H2("Annotation UI");
title.addClassName("main-layout__title");
canvas = new Canvas(1580, 700);
ctx = canvas.getContext();
Div buttons = new Div();
buttons.add(new NativeButton("Save Annotations"));
buttons.add(new NativeButton("Previous Picture"));
buttons.add(new NativeButton("Next Picture"));
buttons.add(new NativeButton("Undo", e -> Canvas.undoLast()));
buttons.add(new NativeButton("Clear Canvas",
e -> ctx.clearRect(0, 0, 1580, 700)));
add(canvas, buttons);
Label label = new Label();
canvas.addComponent(label);
add(label);
// HorizontalLayout fieldLayout = new HorizontalLayout();
TextField boxname = new TextField();
boxname.setLabel("Box Name:");
boxname.setPlaceholder("Enter bounding box name");
TextField boxcategory = new TextField();
boxcategory.setLabel("Box Category:");
boxcategory.setPlaceholder("Enter bounding box category");
add(boxname, boxcategory);
Button submitButton = new Button("Submit");
submitButton.addClickListener(event -> {
if (count == 0){
count = count + 1;
}
bb.get(count - 1).setName(boxname.getValue());
bb.get(count - 1).setBoxcategory(boxcategory.getValue());
count = count + 1;
boxname.clear();
boxcategory.clear();
System.out.println(bb.toString());
});
add(submitButton);
canvas.addMouseMoveListener(() -> label.setText("Coordinates: " + mousePosArray.get(0)));
}
}
Canvas.java:
package com.vaadin.starter.beveragebuddy.ui.components;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.HasSize;
import com.vaadin.flow.component.HasStyle;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.dom.Element;
import com.vaadin.flow.dom.ElementFactory;
import com.vaadin.flow.shared.Registration;
import elemental.json.JsonObject;
import java.util.ArrayList;
import java.util.List;
/**
* Canvas component that you can draw shapes and images on. It's a Java wrapper
* for the
* <a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API">HTML5
* canvas</a>.
* <p>
* Use {@link #getContext()} to get API for rendering shapes and images on the
* canvas.
* <p>
*/
@Tag("canvas")
@SuppressWarnings("serial")
public class Canvas extends Component implements HasStyle, HasSize {
private static CanvasRenderingContext2D context;
private Element element;
private boolean isDrawing = false;
private boolean mouseIsDown = false;
private double endX;
private double endY;
public static ArrayList <BoundingBox> arrayBoxes = new ArrayList<BoundingBox>();
public static ArrayList <MousePosition> mousePosArray = new ArrayList<MousePosition>();
private List<Runnable> mouseMoveListeners = new ArrayList<>(0);
public static ArrayList<BoundingBox> getArrayBoxes() {
return arrayBoxes;
}
public static ArrayList<MousePosition> getMousePosArray() {
return mousePosArray;
}
public static void setMousePosArray(ArrayList<MousePosition> mousePosArray) {
Canvas.mousePosArray = mousePosArray;
}
/**
* Creates a new canvas component with the given size.
* <p>
* Use the API provided by {@link #getContext()} to render graphics on the
* canvas.
* <p>
* The width and height parameters will be used for the canvas' coordinate
* system. They will determine the size of the component in pixels, unless
* you explicitly set the component's size with {@link #setWidth(String)} or
* {@link #setHeight(String)}.
*
// * @param width
// * the width of the canvas
// * @param height
// * the height of the canvas
// */
public Registration addMouseMoveListener(Runnable listener) {
mouseMoveListeners.add(listener);
return () -> mouseMoveListeners.remove(listener);
}
public Canvas(int width, int height) {
context = new CanvasRenderingContext2D(this);
element = getElement();
element.getStyle().set("border", "1px solid");
getElement().setAttribute("width", String.valueOf(width));
getElement().setAttribute("height", String.valueOf(height));
element.addEventListener("mousedown", event -> { // Retrieve Starting Position on MouseDown
Element boundingBoxResult = ElementFactory.createDiv();
element.appendChild(boundingBoxResult);
JsonObject evtData = event.getEventData();
double xBox = evtData.getNumber("event.x");
double yBox = evtData.getNumber("event.y");
boundingBoxResult.setAttribute("data-x", String.format("%f", xBox));
boundingBoxResult.setAttribute("data-y", String.format("%f", yBox));
BoundingBox newBox = new BoundingBox("","", xBox, yBox, 0.0, 0.0);
arrayBoxes.add(newBox);
isDrawing = true;
mouseIsDown=true;
mouseMoveListeners.forEach(Runnable::run);
}).addEventData("event.x").addEventData("event.y");
element.addEventListener("mouseup", event -> { // Draw Box on MouseUp
Element boundingBoxResult2 = ElementFactory.createDiv();
element.appendChild(boundingBoxResult2);
JsonObject evtData2 = event.getEventData();
endX = evtData2.getNumber("event.x");
endY = evtData2.getNumber("event.y");
boundingBoxResult2.setAttribute("end-x", String.format("%f", endX));
boundingBoxResult2.setAttribute("end-y", String.format("%f", endY));
double xcoordi = 0;
double ycoordi = 0;
double boxWidth = 0;
double boxHeight = 0;
for (int i = 0; i < arrayBoxes.size(); i++) {
arrayBoxes.get(i).setWidth(endX, arrayBoxes.get(i).xcoordi);
arrayBoxes.get(i).setHeight(endY, arrayBoxes.get(i).ycoordi);
xcoordi = arrayBoxes.get(i).getXcoordi();
ycoordi = arrayBoxes.get(i).getYcoordi();
boxWidth = arrayBoxes.get(i).getWidth();
boxHeight = arrayBoxes.get(i).getHeight();
}
mouseIsDown=false;
context.beginPath();
context.setStrokeStyle("green");
context.setLineWidth(2);
context.strokeRect(xcoordi, ycoordi, boxWidth, boxHeight);
context.stroke();
context.fill();
System.out.println(arrayBoxes.toString());
mouseMoveListeners.forEach(Runnable::run);
}).addEventData("event.x").addEventData("event.y");
element.addEventListener("mousemove", event -> { // Retrieve Mouse Position when Moving
JsonObject mousePos = event.getEventData();
double mouseX = mousePos.getNumber("event.x");
double mouseY = mousePos.getNumber("event.y");
MousePosition currentPos = new MousePosition(mouseX, mouseY);
mousePosArray.add(0, currentPos);
setMousePosArray(mousePosArray);
mouseMoveListeners.forEach(Runnable::run);
}).addEventData("event.x").addEventData("event.y");
}
public static void undoLast() {
if (arrayBoxes.size() > 0) {
arrayBoxes.remove(arrayBoxes.size() - 1);
}
// System.out.println(arrayBoxes.toString());
System.out.println(arrayBoxes.get(0).toString());
System.out.println(arrayBoxes.size());
for (int i = 0; i < arrayBoxes.size(); i++){
context.clearRect(0, 0, 1580, 700);
context.beginPath();
context.setStrokeStyle("limegreen");
context.setLineWidth(2);
context.strokeRect(arrayBoxes.get(i).xcoordi, arrayBoxes.get(i).ycoordi, arrayBoxes.get(i).boxWidth, arrayBoxes.get(i).boxHeight);
context.fill();
}
}
/**
* Gets the context for rendering shapes and images in the canvas.
* <p>
* It is a Java wrapper for the <a href=
* "https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D">same
* client-side API</a>.
*
* @return the 2D rendering context of this canvas
*/
public CanvasRenderingContext2D getContext() {
return context;
}
/**
* {@inheritDoc}
* <p>
* <b>NOTE:</b> Canvas has an internal coordinate system that it uses for
* drawing, and it uses the width and height provided in the constructor.
* This coordinate system is independent of the component's size. Changing
* the component's size with this method may scale/stretch the rendered
* graphics.
*/
@Override
public void setWidth(String width) {
HasSize.super.setWidth(width);
}
/**
* {@inheritDoc}
* <p>
* <b>NOTE:</b> Canvas has an internal coordinate system that it uses for
* drawing, and it uses the width and height provided in the constructor.
* This coordinate system is independent of the component's size. Changing
* the component's size with this method may scale/stretch the rendered
* graphics.
*/
@Override
public void setHeight(String height) {
HasSize.super.setHeight(height);
}
/**
* {@inheritDoc}
* <p>
* <b>NOTE:</b> Canvas has an internal coordinate system that it uses for
* drawing, and it uses the width and height provided in the constructor.
* This coordinate system is independent of the component's size. Changing
* the component's size with this method may scale/stretch the rendered
* graphics.
*/
@Override
public void setSizeFull() {
HasSize.super.setSizeFull();
}
public void addComponent(Label label) {
}
}
CanvasRenderingContext2D.java:
package com.vaadin.starter.beveragebuddy.ui.components;
import com.vaadin.starter.beveragebuddy.backend.MainLayout;
import java.io.Serializable;
import java.util.ArrayList;
/**
* The context for rendering shapes and images on a canvas.
* <p>
* This is a Java wrapper for the <a href=
* "https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D">same
* client-side API</a>.
*/
public class CanvasRenderingContext2D {
private Canvas canvas;
public static ArrayList<BoundingBox> arrayBoxes = new ArrayList<BoundingBox>();
protected CanvasRenderingContext2D(Canvas canvas) {
this.canvas = canvas;
}
public void setFillStyle(String fillStyle) {
setProperty("fillStyle", fillStyle);
}
public void setStrokeStyle(String strokeStyle) {
setProperty("strokeStyle", strokeStyle);
}
public void setLineWidth(double lineWidth) {
setProperty("lineWidth", lineWidth);
}
public void setFont(String font) {
setProperty("font", font);
}
public void arc(double x, double y, double radius, double startAngle,
double endAngle, boolean antiClockwise) {
callJsMethod("arc", x, y, radius, startAngle, endAngle, antiClockwise);
}
public void beginPath() {
callJsMethod("beginPath");
}
public void clearRect(double x, double y, double width, double height) {
callJsMethod("clearRect", x, y, width, height);
Canvas.arrayBoxes.clear();
System.out.println(arrayBoxes.toString());
MainLayout.count = 0;
}
public void closePath() {
callJsMethod("closePath");
}
/**
* Fetches the image from the given location and draws it on the canvas.
* <p>
* <b>NOTE:</b> The drawing will happen asynchronously after the browser has
* received the image.
*
* @param src
* the url of the image to draw
* @param x
* the x-coordinate of the top-left corner of the image
* @param y
* the y-coordinate of the top-left corner of the image
*/
public void drawImage(String src, double x, double y) {
runScript(String.format(
"var zwKqdZ = new Image();" + "zwKqdZ.onload = function () {"
+ "$0.getContext('2d').drawImage(zwKqdZ, %s, %s);};"
+ "zwKqdZ.src='%s';",
x, y, src));
}
/**
* Fetches the image from the given location and draws it on the canvas.
* <p>
* <b>NOTE:</b> The drawing will happen asynchronously after the browser has
* received the image.
*
* @param src
* the url of the image to draw
* @param x
* the x-coordinate of the top-left corner of the image
* @param y
* the y-coordinate of the top-left corner of the image
* @param width
* the width for the image
* @param height
* the height for the image
*/
public void drawImage(String src, double x, double y, double width,
double height) {
runScript(String.format("var zwKqdZ = new Image();"
+ "zwKqdZ.onload = function () {"
+ "$0.getContext('2d').drawImage(zwKqdZ, %s, %s, %s, %s);};"
+ "zwKqdZ.src='%s';", x, y, width, height, src));
}
public void fill() {
callJsMethod("fill");
}
public void fillRect(double x, double y, double width, double height) {
callJsMethod("fillRect", x, y, width, height);
}
public void fillText(String text, double x, double y) {
callJsMethod("fillText", text, x, y);
}
public void lineTo(double x, double y) {
callJsMethod("lineTo", x, y);
}
public void moveTo(double x, double y) {
callJsMethod("moveTo", x, y);
}
public void rect(double x, double y, double width, double height) {
callJsMethod("rect", x, y, width, height);
}
public void restore() {
callJsMethod("restore");
}
public void rotate(double angle) {
callJsMethod("rotate", angle);
}
public void save() {
callJsMethod("save");
}
public void scale(double x, double y) {
callJsMethod("scale", x, y);
}
public void stroke() {
callJsMethod("stroke");
}
public void strokeRect(double x, double y, double width, double height) {
callJsMethod("strokeRect", x, y, width, height);
}
public void strokeText(String text, double x, double y) {
callJsMethod("strokeText", text, x, y);
}
public void translate(double x, double y) {
callJsMethod("translate", x, y);
}
protected void setProperty(String propertyName, Serializable value) {
runScript(String.format("$0.getContext('2d').%s='%s'", propertyName,
value));
}
/**
* Runs the given js so that the execution order works with callJsMethod().
* Any $0 in the script will refer to the canvas element.
*/
private void runScript(String script) {
canvas.getElement().getNode().runWhenAttached(
// This structure is needed to make the execution order work
// with Element.callFunction() which is used in callJsMethod()
ui -> ui.getInternals().getStateTree().beforeClientResponse(
canvas.getElement().getNode(),
context -> ui.getPage().executeJavaScript(script,
canvas.getElement())));
}
protected void callJsMethod(String methodName, Serializable... parameters) {
canvas.getElement().callFunction("getContext('2d')." + methodName,
parameters);
}
}
BoundingBox.java:
package com.vaadin.starter.beveragebuddy.ui.components;
public class BoundingBox {
public double xcoordi = 0;
public double ycoordi = 0;
public double boxWidth = 0;
public double boxHeight = 0;
public String boxname;
public String boxcategory;
public BoundingBox(String boxname, String boxcategory, double xcoordi, double ycoordi, double boxWidth, double boxHeight) {
this.boxname = boxname;
this.boxcategory = boxcategory;
this.xcoordi = xcoordi;
this.ycoordi = ycoordi;
this.boxWidth = boxWidth;
this.boxHeight = boxHeight;
}
public String getBoxName() {
return boxname;
}
public void setName(String boxname) {
this.boxname = boxname;
}
public String getBoxcategory() {
return boxcategory;
}
public void setBoxcategory(String boxcategory) {
this.boxcategory = boxcategory;
}
public double getXcoordi() {
return xcoordi;
}
public void setXcoordi(double xcoordi) {
this.xcoordi = xcoordi;
}
public double getYcoordi() {
return ycoordi;
}
public void setYcoordi(double ycoordi) {
this.ycoordi = ycoordi;
}
public double getWidth() {
return boxWidth;
}
public void setWidth(double endX, double xcoordi) {
boxWidth = endX - xcoordi;
}
public double getHeight() {
return boxHeight;
}
public void setHeight(double endY, double ycoordi) {
boxHeight = endY - ycoordi;
}
@Override
public String toString() {
return "{" +
"Name=" + boxname +
", Class=" + boxcategory +
", X=" + xcoordi +
", Y=" + ycoordi +
", Width=" + boxWidth +
", Height=" + boxHeight +
'}';
}
}
MousePosition.java:
package com.vaadin.starter.beveragebuddy.ui.components;
public class MousePosition {
public double mouseX;
public double mouseY;
public MousePosition(double mouseX, double mouseY) {
this.mouseX = mouseX;
this.mouseY = mouseY;
}
public double getMouseX() {
return mouseX;
}
public void setMouseX(double mouseX) {
this.mouseX = mouseX;
}
public double getMouseY() {
return mouseY;
}
public void setMouseY(double mouseY) {
this.mouseY = mouseY;
}
@Override
public String toString() {
return "X = " + mouseX +
", Y = " + mouseY;
}
}
堆栈跟踪:
[qtp1975873209-21] ERROR com.vaadin.flow.server.DefaultErrorHandler -
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:657)
at java.util.ArrayList.get(ArrayList.java:433)
at com.vaadin.starter.beveragebuddy.ui.components.Canvas.undoLast(Canvas.java:191)
at com.vaadin.starter.beveragebuddy.backend.MainLayout.lambda$new$9b1b5227$1(MainLayout.java:67)
at com.vaadin.flow.component.ComponentEventBus.fireEvent(ComponentEventBus.java:133)
at com.vaadin.flow.component.ComponentEventBus.handleDomEvent(ComponentEventBus.java:327)
at com.vaadin.flow.component.ComponentEventBus.lambda$addDomTrigger$5ee67f2b$1(ComponentEventBus.java:191)
at com.vaadin.flow.internal.nodefeature.ElementListenerMap.lambda$fireEvent$2(ElementListenerMap.java:379)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at com.vaadin.flow.internal.nodefeature.ElementListenerMap.fireEvent(ElementListenerMap.java:379)
at com.vaadin.flow.server.communication.rpc.EventRpcHandler.handleNode(EventRpcHandler.java:59)
at com.vaadin.flow.server.communication.rpc.AbstractRpcInvocationHandler.handle(AbstractRpcInvocationHandler.java:64)
at com.vaadin.flow.server.communication.ServerRpcHandler.handleInvocationData(ServerRpcHandler.java:377)
at com.vaadin.flow.server.communication.ServerRpcHandler.lambda$handleInvocations$0(ServerRpcHandler.java:367)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at com.vaadin.flow.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:367)
at com.vaadin.flow.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:309)
at com.vaadin.flow.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:89)
at com.vaadin.flow.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:40)
at com.vaadin.flow.server.VaadinService.handleRequest(VaadinService.java:1487)
at com.vaadin.flow.server.VaadinServlet.service(VaadinServlet.java:300)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:812)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1669)
at org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:201)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:585)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:577)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:223)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1127)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1061)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:215)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
at org.eclipse.jetty.server.Server.handle(Server.java:499)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:311)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:258)
at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:544)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)
at java.lang.Thread.run(Thread.java:748)
非常感谢您的帮助,谢谢!
答案 0 :(得分:2)
在撤消方法中,您具有以下代码
WP_CRON?
您的for (int i = 0; i < arrayBoxes.size(); i++){
context.clearRect(0, 0, 1580, 700);
context.beginPath();
context.setStrokeStyle("limegreen");
context.setLineWidth(2);
context.strokeRect(arrayBoxes.get(i).xcoordi, arrayBoxes.get(i).ycoordi, arrayBoxes.get(i).boxWidth, arrayBoxes.get(i).boxHeight);
context.fill();
}
函数包含该行
context.clearRect()
从Canvas.arrayBoxes.clear();
数组中删除所有项目。因此,当您尝试执行arrayBoxes
等时,在for循环的末尾会出现异常。