每次单击按钮时如何创建一个新对象(矩形)?

How to create a new object(rectangle) everytime when the button is clicked?

我想创建一个形状对象(在我的例子中,对象是一个矩形)。每次我点击一个按钮。目前,我可以让它只出现一次。我的想法是,每次我单击按钮时,都会创建一个新的矩形对象,作为旧对象的补充。因此,如果我点击按钮 5 次,我应该有 5 个矩形。

我试着用 ArrayList 来做,但仍然只出现一个矩形。有人知道怎么做吗!

非常感谢您!

这是主要的 class,仅供参考还有一个矩形 Class(未附加)

    import controlP5.*;
    ControlP5 cp5;
    
    Rectangle rect; // rect begins as null
    Button rc;
    
    ArrayList<Rectangle> rectList;
    
    void setup(){
    
    size(1000, 1000);
    rectList = new ArrayList<Rectangle>();
     
    cp5 = new ControlP5(this);
    
    rc = cp5.addButton("Rectangle").
             setPosition(5, 4).
             setColorBackground(color(52, 55, 76));
             
    rc.onRelease(new CallbackListener() {    
        public void controlEvent(CallbackEvent theEvent) {
          // only create the rectangle when the button is clicked
          rect = new Rectangle(100, 100, 100, 100);
        }
    });
    
    }
    
    void draw(){
    background(255);
    
    // if the rect exists, draw it on the screen
      if(rect != null) {
         rect.displayRect();
         showRect();
      } 
      
      for(int i = 0; i < rectList.size(); i++){
        //((Rectangle)rectList.get(i)).update();
        ((Rectangle)rectList.get(i)).displayRect();
      }  
    }
    
    public void showRect(){
      for(Rectangle r: rectList){
      r.displayRect();
      rect(r.getXvalue(), r.getYvalue(), r.getWvalue(), r.getHvalue());
    }
    
    }

您有一个列表,但您从未向该列表添加任何内容。该列表仍然是空的。

删除成员字段 rect,删除此行:

Rectangle rect; // rect begins as null

当您实例化一个新的 Rectangle 时,立即将其添加到列表中。

rc.onRelease( new CallbackListener() {
    
    public void controlEvent(CallbackEvent theEvent) {
      // When the button is clicked, instantiate a new rectangle and remember it by adding to our list of rectangles. 
      rectList.add(
          new Rectangle( 100, 100, 100, 100 )
      );
    }
});

一些责备:这不是 Stack Overflow 的好问题。通过使用调试器单步执行代码,您可以很容易地发现此错误。您会看到该列表仍然是空的。发帖前自己调试一下。

您应该 post 您的 Rectangle class 以便其他人更容易测试和提供帮助。

正如 Basil 所指出的 (+1),当发生点击事件时,您只会为一帧渲染一个新的矩形。

The idea would be that each time I click the button, a new rectangle Object is created, additional to the old one. Therefore, if I click the button 5 times, I should have 5 rectangles.

这个说法有点模棱两可。我知道您想在每次点击时渲染一个矩形,但是在点击处理程序中,该矩形具有完全相同的尺寸和坐标。即使您会进行小的修复,将 5 个相同的矩形彼此重叠渲染也可能看起来就像是一个矩形。

关于您 post 编写的代码,这对我来说很突出:

  • Rectangle rect; // rect begins as null:如果下面有这个矩形的目的是什么:ArrayList<Rectangle> rectList;
  • showRect();draw() 中被调用:它遍历 rectList 并且不仅调用 displayRect() 我认为会呈现当前矩形,而且还会重新- 在下一行呈现相同的数据 (rect(r.getXvalue(), r.getYvalue(), r.getWvalue(), r.getHvalue());)
  • 下面,在同一个列表上有一个 for 循环再次调用 displayRect()。我的猜测是 3 次渲染矩形的调用中有 2 次是多余的。 (因此数组列表也是类型化的,不需要像这样转换:(Rectangle)rectList.get(i)),rectList.get(i) 就足够了)

我唯一的其他小警告是关于命名:理想情况下,您希望在处理中坚持使用 Java naming conventions。 (例如 getXValue() 而不是 getXvalue(),等等)

关于 ControlP5 按钮,您可以使用 controlEvent(),这比设置回调要简单一些。更简单的是使用这种自动变量堵塞功能。简而言之,如果一个函数与按钮的名称同名,它将被自动调用:

Automatic controller-event detection ControlP5 offers a range of controllers that allow you to easily change and adjust values while your sketch is running. Each controller is identified by a unique name assigned when creating a controller. ControlP5 locates variables and functions inside your sketch and will link controllers to matching variables or functions automatically

(来自controlP5 reference

这是一个基本示例,每次单击按钮时都会向控制台打印一条消息:

import controlP5.*;
ControlP5 cp5;

void setup() {

  size(1000, 1000);
  
  cp5 = new ControlP5(this);
  
  cp5.addButton("rectangle").
    setPosition(5, 4).
    setColorBackground(color(52, 55, 76));
}

void draw(){
  background(255);
}

void rectangle(){
  println("rectangle button clicked");
}

(我保留了名称 rectangle 而不是 Rectangle 以与 Java 命名约定保持一致。文本标签无论如何都以大写显示)

回到你的主要问题,如果你想在每次按下按钮时添加新的矩形并渲染它们,代码就像这样简单:

import controlP5.*;
ControlP5 cp5;

ArrayList<Rectangle> rectList = new ArrayList<Rectangle>();

int x = 100;
int y = 100;

void setup() {

  size(1000, 1000);
  rectList = new ArrayList<Rectangle>();

  cp5 = new ControlP5(this);

  cp5.addButton("rectangle").
  setPosition(5, 4).
  setColorBackground(color(52, 55, 76));

}

void draw() {
  background(255);

  for (int i = 0; i < rectList.size(); i++) {
    rectList.get(i).display();
  }
}

void rectangle(){
  rectList.add(new Rectangle(x, y, 100, 100));
  // increment x, y to avoid superimposed rectangles
  x += 50;
  y += 50;
}

class Rectangle{
  
  private int x, y, w, h;
  
  Rectangle(int x, int y, int w, int h){
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
  }
  
  void display(){
    rect(x, y, w, h);
  }
  // currently not used
  public int getX(){
    return x;
  }
  
  public int getY(){
    return x;
  }
  
  public int getWidth(){
    return x;
  }
  
  public int getHeight(){
    return x;
  }
  
}

Rectangle rect;脱颖而出。如果这是一项 OOP 家庭作业或练习,也许其目的是提供一个基本的绘图应用程序功能,用户可以在其中使用矩形绘图工具?

如果是这样,rect 可能是可以克隆到 rectList 中的选择矩形,因此它会持续存在。

您可以像这样实现矩形选择:

  1. 按下鼠标时记住坐标:这些是选择的起点
  2. 随着鼠标拖动,终点坐标为当前鼠标坐标,因此选择矩形尺寸为当前鼠标坐标与先前存储的鼠标坐标之间的差值
  3. 释放鼠标时,重置选择矩形(因此不再显示)

这是一个基本的示例草图:

Rectangle selection = new Rectangle(0, 0, 0, 0);

void setup(){
  size(1000, 1000);
}

void draw(){
  background(255);
  selection.display();
}

void mousePressed(){
  // store selection start
  selection.x = mouseX;
  selection.y = mouseY;
}

void mouseDragged(){
  // update selection dimension as the difference between the current mouse coordinates and the previous ones (selection x, y)
  selection.w = mouseX - selection.x;
  selection.h = mouseY - selection.y;
}

void mouseReleased(){
  selection.w = selection.h = selection.x = selection.y = 0;
}

class Rectangle{
  
  private int x, y, w, h;
  
  Rectangle(int x, int y, int w, int h){
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
  }
  
  void display(){
    rect(x, y, w, h);
  }
  
}

Rectangle 属性重置为 0 可以很好地封装到一个方法中:

void reset(){
    x = y = w = h = 0;
  }

释放处理程序也可用于向 rectList 添加一个新矩形,该矩形具有与选择相同的属性 (x, y, w, h),但在重置选择之前。类似于:

void mouseReleased(){
  // add a copy of the selection to rectList
  rectList.add(new Rectangle(selection.x, selection.y, selection.w, selection.h));
  // reset selection
  selection.reset();
}

同样,复制功能也可以很好地封装为一种方法:

Rectangle copy(){
    return new Rectangle(x, y, w, h);
  }

将它们放在一起看起来像这样:

import controlP5.*;
ControlP5 cp5;

ArrayList<Rectangle> rectList = new ArrayList<Rectangle>();

Rectangle selection = new Rectangle(0, 0, 0, 0);

void setup() {

  size(1000, 1000);
  rectList = new ArrayList<Rectangle>();

  cp5 = new ControlP5(this);

  cp5.addButton("rectangle").
  setPosition(5, 4).
  setColorBackground(color(52, 55, 76));

}

void draw() {
  background(255);
  // draw previous rectangles (black)
  stroke(0);
  for (int i = 0; i < rectList.size(); i++) {
    rectList.get(i).display();
  }
  // draw current selection (green)
  stroke(0, 192, 0);
  selection.display();
}

void rectangle(){
  println("selected drawing tool is rectangle");
}

void mousePressed(){
  // store selection start
  selection.x = mouseX;
  selection.y = mouseY;
}

void mouseDragged(){
  // update selection dimension as the difference between the current mouse coordinates and the previous ones (selection x, y)
  selection.w = mouseX - selection.x;
  selection.h = mouseY - selection.y;
}

void mouseReleased(){
  // add a new rectangle to the list: a copy of the selection
  rectList.add(selection.copy());
  // reset selection
  selection.reset();
}

class Rectangle{
  
  private int x, y, w, h;
  
  Rectangle(int x, int y, int w, int h){
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
  }
  
  void display(){
    rect(x, y, w, h);
  }
  
  Rectangle copy(){
    return new Rectangle(x, y, w, h);
  }
  
  void reset(){
    x = y = w = h = 0;
  }

}

在这个阶段这个按钮有点多余,但是如果将来需要其他形状它可能会有用(例如 Ellipse 显然是一个很容易实现的按钮,因为 ellipse() 有与 rect() 相同的参数,只需要确保 ellipseMode(CORNER) 设置为使用与矩形相同的选择 x,y 坐标)

希望这有用。如前所述,初始代码看起来有点乱,好像是在截止日期前匆忙拼凑起来的。 (我假设这是因为它让我想起了我作为学生时的代码 :)) 我建议离开计算机走一小段路,记住任务是什么,然后用笔在纸上将任务分解成小的、清晰的、易于实施的子任务。一旦尽可能清楚,一次单独执行一个子任务。最初代码可能会中断或变得混乱,但最终它会起作用(与整个绘图程序相比,它会更容易编写)。一旦成功,清理代码以便删除所有不必要的代码,并且很容易将代码移动到另一个草图并 运行 它没有错误。对每个子任务重复该过程,这将导致多个最小草图解决一个问题。一旦所有部分都单独解决,您就可以开始将代码放在一个主草图中,但是我建议一次添加一个子任务代码并先进行测试。当混合来自多个草图的代码时 conflicts/errors 可能会出现,并且一次合并两个草图比一次性处理所有子任务草图要容易得多。祝你好运!