如何在按钮点击时调用函数?

3
我是一位有用的助手,可以为您翻译文本。
我有一个名为Button的Processing类,其中包含以下函数onClick:
class Button() {
    // Extra code
    void onClick() {
        // Run function here
    }
}

当点击按钮时,它应该触发一个函数调用,该函数从参数中获取,如下所示:

Button test = new Button(..., callFunction())

callFunction()作为一个void函数被保证时,我如何在test被点击后调用函数callFunction()? 我可以检查按钮是否被点击,但我不知道如何将其连接到由参数提供的函数调用。


1
Java的核心是类和接口。即使是lambda表达式也被映射到接口上。因此没有方法或函数引用。您必须使用Runnable或Consumer来定义函数。 - wumpz
4个回答

4
使用 Runnable 接口。
Button test = new Button(..., new Runnable(){

     public void run() {
         // your stuff.
         callFunction();
     }
});

然后:

class Button() {

    private Runnable runnable;

    public Button (... Runnable runnable ){
       this.runnable  = runnable;
    }

    // Extra code
    void onClick() {
        runnable.start();
    }
}

我需要使用import来使用这个吗?我应该使用哪个import? - Qwerp-Derp
这是标准的Java:http://docs.oracle.com/javase/7/docs/api/java/lang/Runnable.html - Mariano L
在onClick中,“runnable”是什么?从我看到的来看,它是一个未定义的变量。 - Qwerp-Derp
好的,在构造函数中,您传递了一个可运行对象。因此,您应该将其保存为类对象的私有成员。我已经编辑了我的示例。 - Mariano L
1
这个不起作用 - 在runnable.start()行上处理错误,出现某些原因导致start()不是一个函数的提示。 - Qwerp-Derp
1
我认为应该是 runnable.run() - walen

1
我不确定这是否符合您的目的。但是,外部处理程序可能会很方便。
class Button {
    // Extra code
    private ClickHandler handler;

    //Constructor
    Button(..., ClickHandler handler) {
        this.handler = handler;
    }
    void onClick() {
    // Run function here
        handler.handle();
    }
}

interface ClickHandler {
    void handle();
}

现在你可以像这样创建你的对象:

Button test = new Button(..., new ClickHandler() {
    public void handle() {
        //do your stuff.
    }
});


注意:可能Java 8通过lambda支持回调函数引用。

那个需要是public void - 但除此之外,它可以工作!谢谢! - Qwerp-Derp
@Qwerp-Derp,我已经编辑了代码,感谢您指出问题!如果有帮助的话,您可能也想接受这个答案 :-) - abksrv

0

请查看处理按钮示例

它没有封装成一个类,这取决于您的实现, 但它确实演示了如何使用Processing内置的draw()mousePressed()方法来处理事件。

如果有帮助的话,这是我在一些我的草图中使用的按钮类:

int numButtons = 3;
Button[] buttons = new Button[numButtons];

void setup(){
  size(180,200);

  int buttonWidth = 150;
  int buttonHeight = 50;
  int buttonSpacing = 15;

  for(int i = 0 ; i < numButtons; i++){
    buttons[i] = new Button("button "+(i+1),buttonSpacing,(buttonHeight+buttonSpacing) * i,buttonWidth,buttonHeight);  
  }
}
void draw(){
  background(255);

  for(int i = 0 ; i < numButtons; i++){
    buttons[i].update(mouseX, mouseY, mousePressed);
    buttons[i].draw();
  }
}
//hacky way callback the main sketch from a button - look into callbacks
void onButtonClicked(Button b){
  println(b.label,"was pressed at",new java.util.Date());
}

class Button{
  float w,h,x,y;//width, height and position
  color bg = color(200);//background colour
  color fg = color(0);//foreground colour
  String label;//button label

  boolean isOver,wasPressed;
  int pw = 10;
  Button(String label,float x,float y,float w,float h){
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.label = label;
  }
  //mouseY,mouseY,mouseDown
  void update(int mx,int my,boolean md){
    //it's over if it's within the button's bounding box
    isOver = ((mx >= x && mx <= (x+w))&&(my >= y && my <= (y+h)));
    //if it's over and pressed it's a click
    if(isOver && md){
      //if it wasn't pressed before, trigger click callback
      if(!wasPressed){
        onButtonClicked(this);
        wasPressed = true;
      }
    }else wasPressed = false;
  }

  void draw(){
    pushStyle();
      noStroke();
      fill(isOver ? fg : bg);
      rect(x,y,w,h);
      fill(isOver ? bg : fg);
      text(label,x+pw,y+h*.75);
    popStyle();
  }
}

你可能需要以不同的方式实现它,但希望这能给你一些想法。

更新 如果感觉回调是你主要关注的事情。 如果是这样,一个选项是使用Java反射来调用方法

这是上面Button类的修改版本,它接受包含回调方法和回调方法名称的类作为参数:

import java.lang.reflect.*;

int numButtons = 3;
Button[] buttons = new Button[numButtons];

void setup(){
  size(180,200);

  int buttonWidth = 150;
  int buttonHeight = 50;
  int buttonSpacing = 15;

  for(int i = 0 ; i < numButtons; i++){
    buttons[i] = new Button(this,"onButton"+(i+1)+"Clicked","button "+(i+1),buttonSpacing,(buttonHeight+buttonSpacing) * i,buttonWidth,buttonHeight);  
  }
}
void draw(){
  background(255);

  for(int i = 0 ; i < numButtons; i++){
    buttons[i].update(mouseX, mouseY, mousePressed);
    buttons[i].draw();
  }
}
public void onButton1Clicked(){
  println("button 1 clicked");
}
public void onButton2Clicked(){
  println("button 2 clicked");
}
public void onButton3Clicked(){
  println("button 3 clicked");
}

class Button{
  float w,h,x,y;//width, height and position
  color bg = color(200);//background colour
  color fg = color(0);//foreground colour
  String label;//button label

  boolean isOver,wasPressed;
  int pw = 10;

  Object parent;
  Method callback;

  Button(Object parent,String callbackName,String label,float x,float y,float w,float h){

    this.parent = parent; 
    try{
      callback = parent.getClass().getMethod(callbackName);
    }catch(Exception e){
      e.printStackTrace();
    }

    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.label = label;
  }
  //mouseY,mouseY,mouseDown
  void update(int mx,int my,boolean md){
    //it's over if it's within the button's bounding box
    isOver = ((mx >= x && mx <= (x+w))&&(my >= y && my <= (y+h)));
    //if it's over and pressed it's a click
    if(isOver && md){
      //if it wasn't pressed before, trigger click callback
      if(!wasPressed){
        //onButtonClicked(this);
        if(callback != null){
           try{
            callback.invoke(parent);
          }catch(Exception e){
            e.printStackTrace();
          }
        }
        wasPressed = true;
      }
    }else wasPressed = false;
  }

  void draw(){
    pushStyle();
      noStroke();
      fill(isOver ? fg : bg);
      rect(x,y,w,h);
      fill(isOver ? bg : fg);
      text(label,x+pw,y+h*.75);
    popStyle();
  }
}

这是解决问题的一种方式。如果您需要为这些回调函数提供参数,请务必深入了解Java反射和Method类。


1
这就是我计划完成这个问题后要做的事情 - 另外,你还没有回答我的问题,我认为你有点绕弯子了。 - Qwerp-Derp
如果回调函数是您关注的主要内容,请查看上面更新的答案。 - George Profenza

0

我假设你正在使用Java 8。如果你还没有使用,那么你应该开始使用了!因为它相比于旧版本的主要改进之一就是可以将函数作为参数进行处理。

要将函数对象(即lambda表达式)作为参数传递,首先需要定义一个函数式接口来“持有”函数的实现。函数式接口可以是任何只有一个方法的接口,例如:

interface MyFunction {
    public void call();
}

无论你在哪里声明它,甚至是在你的Button类内部。只要Button能够访问到它就可以。

然后,你只需要告诉你的Button构造函数,最后一个参数是一个函数,将其存储为一个私有字段,并在按钮被点击时调用它:

class Button() {
    MyFunction buttonFunction;
    public Button(..., MyFunction myFunction) {
        // ... other initialization ...
        buttonFunction = myFunction;
    }
    // Extra code
    void onClick() {
        // Run function here
        buttonFunction.call();
    }
}

如果要将您的函数作为参数传递,请按照以下方式进行:

Button test = new Button(..., () -> callFunction());

您无需创建匿名的MyFunction类或覆盖任何内容。Java 8会为您解决所有这些问题。它知道Button构造函数需要一个MyFunction参数,并且它知道MyFunction只有一个没有参数的方法call(); 所以当您提供一个没有参数的函数体时,Java知道它是MyFunction#call()的实现。

事实上,您甚至不需要定义接口:您只需将MyFunction替换为Runnable,将call()替换为run(),一切都将按预期工作,因为Runnable也是一个功能接口:

class Button() {
    Runnable buttonFunction;
    public Button(..., Runnable myFunction) {
        // ... other initialization ...
        buttonFunction = myFunction;
    }
    // Extra code
    void onClick() {
        // Run function here
        buttonFunction.run();
    }
}

Button test = new Button(..., () -> callFunction());

但最好先了解基础知识!


遗憾的是,Processing不支持Java 8... :( - Qwerp-Derp
很抱歉听到这个。在这种情况下,Mariano的答案是最好的——只需将start()替换为run() - walen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接