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

问题描述

我想创建一个形状对象(在我的情况下,对象是一个矩形)。每次我点击一个按钮。目前,我只能让它出现一次。这个想法是每次我点击按钮时,都会创建一个新的矩形对象,除了旧的对象。因此,如果我点击按钮 5 次,我应该有 5 个矩形。

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

在此先非常感谢您!

这是主类,仅供参考,还有一个矩形类(未附上)

    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);
        }
    });
    
    }
    
    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 )
      );
    }
});

有些责备:这对 Stack Overflow 来说不是一个好问题。您可以通过使用调试器单步调试代码轻松地发现此错误。您会看到该列表仍然是空的。发帖前,自己调试一遍。

,

您应该发布您的 Rectangle 类,以便其他人更轻松地进行测试和提供帮助。

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

这个想法是每次我点击按钮时,都会创建一个新的矩形对象,除了旧的对象。因此,如果我点击按钮 5 次,我应该有 5 个矩形。

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

关于您发布的代码,这对我来说很突出:

  • 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) 应该就足够了)

我唯一的另一个小问题是命名:理想情况下,您希望在 Processing 中坚持使用 Java naming conventions。 (例如 getXValue() 而不是 getXvalue() 等)

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

自动控制器事件检测 ControlP5 提供了一系列控制器,允许您在草图运行时轻松更改和调整值。每个控制器由创建控制器时分配的唯一名称标识。 ControlP5 在您的草图中定位变量和函数,并将控制器自动链接到匹配的变量或函数

(来自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,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));
  // increment x,y to avoid superimposed rectangles
  x += 50;
  y += 50;
}

class Rectangle{
  
  private int x,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,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; 脱颖而出。如果这是一个面向对象编程的家庭作业或练习,其目的可能是拥有一个基本的绘图应用程序功能,用户可以在其中使用矩形绘图工具?

如果是这种情况,rect 可能是选择矩形,它可以克隆到 rectList 中,因此它仍然存在。

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

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

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

Rectangle selection = new Rectangle(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,h);
  }
  
}

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

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

释放处理程序也可用于向 rectList 添加一个与选择具有相同属性 (x,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,h);
  }

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

import controlP5.*;
ControlP5 cp5;

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

Rectangle selection = new Rectangle(0,0);

void setup() {

  size(1000,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,h);
  }
  
  Rectangle copy(){
    return new Rectangle(x,h);
  }
  
  void reset(){
    x = y = w = h = 0;
  }

}

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

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