问题描述
我想创建一个形状对象(在我的情况下,对象是一个矩形)。每次我点击一个按钮。目前,我只能让它出现一次。这个想法是每次我点击按钮时,都会创建一个新的矩形对象,除了旧的对象。因此,如果我点击按钮 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 在您的草图中定位变量和函数,并将控制器自动链接到匹配的变量或函数
这是一个基本示例,每次单击按钮时都会向控制台打印一条消息:
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
中,因此它仍然存在。
您可以像这样实现矩形选择:
- 按下鼠标时记住坐标:这些是选择的起点
- 当鼠标被拖动时,终点坐标是当前鼠标坐标,因此选择矩形尺寸是当前鼠标坐标与之前存储的鼠标坐标之间的差
- 释放鼠标后,重置选择矩形(使其不再显示)
这是一个基本的示例草图:
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 坐标)
希望这是有用的。前面提到的初始代码看起来有点乱,好像是在截止日期前匆匆忙忙地拼凑起来的。 (我认为这是因为它让我想起了我作为学生的代码:)) 我建议远离计算机走一小段路,记住任务是什么,然后在纸上用笔将任务分解为小的、清晰的、易于实施的子任务。一旦尽可能清楚,一次单独执行一个子任务。最初代码可能会中断或变得混乱,但最终它会起作用(与整个绘图程序相比,它会更容易编写)。一旦成功,清理代码,以便删除所有不必要的代码,并且很容易将代码移动到另一个草图并运行它而不会出错。对每个子任务重复该过程,这将导致解决一个问题的多个最小草图。一旦单独解决了所有部分,您就可以开始将代码放在一个主草图中,但是我建议添加一个子任务代码作为时间并先进行测试。当混合来自多个草图的代码时,可能会出现冲突/错误,并且一次合并两个草图比一次性处理所有子任务草图要容易得多。祝你好运!