Reputation: 2120
I'm writing an Android app, in which I have several buttons laid out in a grid. I want to set the onClick method of each button, so I wrote this:
for (int i = 0; i < button.length; i++) {
for (int j = 0; j < button[0].length; j++) {
button[i][j].setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
press(i, j);
}
});
}
}
where press(int i, int j)
is implemented elsewhere. I get the error, "Cannot refer to a non-final variable i inside an inner class defined in a different method".
So, right now I just have each function written out, like this:
button[0][0].setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
press(0, 0);
}
});
button[0][1].setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
press(0, 1);
}
});
// more like this...
This works, but it seems silly. Is there a better way?
Upvotes: 4
Views: 1917
Reputation: 10003
you can get by with one listener:
package tayek.so9551773;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.*;
import android.util.Log;
public class So9551773 extends Activity {
void press(int i,int j) {
Log.d("so",i+","+j+" was clicked");
System.out.println(i+","+j+" was clicked");
}
void addButtons(TableLayout tableLayout) {
for(int i=0;i<buttons.length;i++) {
TableRow tableRow=new TableRow(this);
tableLayout.addView(tableRow);
for(int j=0;j<buttons[i].length;j++) {
buttons[i][j]=new Button(this);
tableRow.addView(buttons[i][j]);
buttons[i][j].setText(i+" "+j);
buttons[i][j].setId(i*buttons.length+j);
buttons[i][j].setOnClickListener(onClickListener);
}
}
}
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("so","created");
TableLayout tableLayout=new TableLayout(this);
addButtons(tableLayout);
setContentView(tableLayout);
}
Button[][] buttons=new Button[3][3];
OnClickListener onClickListener=new OnClickListener(){
public void onClick(View v) {
int id=v.getId();
press(id/buttons.length,id%buttons.length);
}
};
}
Upvotes: 1
Reputation: 14025
for (int i = 0; i < button.length; i++) {
for (int j = 0; j < button[0].length; j++) {
final int localI = i;
final int localJ = j;
button[localI][localJ].setOnClickListener(...);
}
}
Upvotes: 2
Reputation: 8153
Yes, try this:
for (int i = 0; i < button.length; i++) {
for (int j = 0; j < button[0].length; j++) {
final int listenerI = i;
final int listenerJ = j;
button[i][j].setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
press(listenerI, listenerJ);
}
});
}
}
Upvotes: 6
Reputation: 27233
You can define a helper method to set the listener for a button:
private static void setListenerForButton(Button button, final int i, final int j) {
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
press(i, j);
}
});
}
and then you can use it like this
for (int i = 0; i < button.length; i++) {
for (int j = 0; j < button[0].length; j++) {
setListenerForButton(button[i][j], i, j);
}
}
This is a special case of a more general solution: define extra final int
variables for use in the inner class and initialize them with the values of i
and j
. In this case the extra final int
variables are arguments to the helper function.
Upvotes: 4
Reputation: 121649
You can easily do something like that in Javascript ... but not Java :)
Here are a couple of good links:
Cannot refer to a non-final variable inside an inner class defined in a different method
http://docs.oracle.com/javase/specs/jls/se5.0/html/classes.html#8.1.3
http://www.javaworld.com/javaworld/jw-06-2008/jw-06-closures.html
Upvotes: 0