Assigning onClickListeners to buttons through a loop - Android

I know how to accomplish this task in a long, subtle way, but I want to shorten it and make it more elegant. Below I will show what I can do in a non-graceful way and how I want to do it in an elegant way. The elegant way does not compile. I have commented where and what the error is.

Any help with the structure of the code is appreciated! Thank you in advance.

Results.java

public class Results extends Activity {
int x = -1;

Button q1details, q2details, q3details, q4details, q5details, q6details, q7details, q8details, q9details, q10details;

final Context context = this;

@Override
public void onCreate(Bundle savedInstanceState) {

    q1details = (Button)findViewById(R.id.q1details);
    q2details = (Button)findViewById(R.id.q2details);
    q3details = (Button)findViewById(R.id.q3details);
    q4details = (Button)findViewById(R.id.q4details);
    q5details = (Button)findViewById(R.id.q5details);
    q6details = (Button)findViewById(R.id.q6details);
    q7details = (Button)findViewById(R.id.q7details);
    q8details = (Button)findViewById(R.id.q8details);
    q9details = (Button)findViewById(R.id.q9details);
    q10details = (Button)findViewById(R.id.q10details);

    buttonList.add(q1details); buttonList.add(q2details); buttonList.add(q3details); buttonList.add(q4details); buttonList.add(q5details); buttonList.add(q6details); buttonList.add(q7details); buttonList.add(q8details); buttonList.add(q9details); buttonList.add(q10details);

    for(int i = 0; i < buttonList.size(); i++) {
        buttonList.get(i).setText("Question Details");
        buttonList.get(i).setOnClickListener(new OnClickListener() {
            public void onClick(View arg0) {
                x = i;  //ERROR:  "Cannot refer to a non-final variable  i inside an inner class defined in a different method."  But I cannot put the i as Final in a for-loop.
                displayDetails();
            }
        });
    }

/*      q1details.setOnClickListener(new OnClickListener() {
        public void onClick(View arg0) {
            x = 0;
            displayDetails();
        }
    });
            //I could do 10 of these to accomplish the task but its non-elegant...
*/

public void displayDetails() {

    final Dialog dialog = new Dialog(context);
    dialog.setContentView(R.layout.questiondetails);
    dialog.setTitle("Question " + (x + 1) + " Details");

    ImageView image = (ImageView)dialog.findViewById(R.id.image);
    TextView correctness = (TextView)dialog.findViewById(R.id.correctness);
    TextView questionHeader = (TextView)dialog.findViewById(R.id.questionHeader);
    TextView question = (TextView)dialog.findViewById(R.id.question);
    TextView selectedAnswerHeader = (TextView)dialog.findViewById(R.id.selectedAnswerHeader);
    TextView selectedAnswer = (TextView)dialog.findViewById(R.id.selectedAnswer);
    TextView correctAnswerHeader = (TextView)dialog.findViewById(R.id.correctAnswerHeader);
    TextView correctAnswer = (TextView)dialog.findViewById(R.id.correctAnswer);
    TextView points = (TextView)dialog.findViewById(R.id.points);
    TextView verseHeader = (TextView)dialog.findViewById(R.id.verseHeader);
    TextView verse = (TextView)dialog.findViewById(R.id.verse);
    Button close = (Button)dialog.findViewById(R.id.close);

    if(qs.get(x).getSelectedAnswer() == qs.get(x).getCorrectAnswer()) {
        image.setBackgroundResource(R.drawable.green_check_mark);
        correctness.setText("CORRECT!");
        correctness.setTextColor(Color.GREEN);
    } else {
        image.setBackgroundResource(R.drawable.red_x);
        correctness.setText("INCORRECT!");
        correctness.setTextColor(Color.RED);
    }

    String a = "<u>QUESTION</u>";
    questionHeader.setText(Html.fromHtml(a));
    question.setText(qs.get(0).getQuery());

    String b = "<u>ANSWER YOU SELECTED</u>";
    selectedAnswerHeader.setText(Html.fromHtml(b));
    selectedAnswer.setText("" + qs.get(0).getStringSelectedAnswer());

    String c = "<u>CORRECT ANSWER</u>";
    correctAnswerHeader.setText(Html.fromHtml(c));
    correctAnswer.setText("" + qs.get(0).getStringCorrectAnswer());

    points.setText("You received " + QuestionView.getPointsPerQuestion().get(x) + " out of a possible 100 points.");

    String d = "<u>PROVE THE ANSWER IN YOUR BIBLE</u>";
    verseHeader.setText(Html.fromHtml(d));
    verse.setText(qs.get(0).getVerse());        

    close.setText("Close");

    close.setOnClickListener(new OnClickListener() {
        public void onClick(View arg0) {
            dialog.dismiss();
        }
    });
    dialog.show();
}

      

+3


source to share


3 answers


End references declared in just the outer scope of an anonymous class are available to that anonymous class. (Java flavor of closures):



for(int i = 0; i < buttonList.size(); i++) {
    Button button = buttonList.get(i);
    button.setText("Question Details");

    final int j = i;

    button.setOnClickListener(new OnClickListener() {
        public void onClick(View arg0) {
            x = j;
            displayDetails();
        }
    });
}

      

+6


source


The code inside onClick()

will run when the button is pressed, not during the loop ... So even if you make it final somehow i

, it won't hold the expected value.

However, you can use common methods setTag()

and getTag()

storage i

.
Something like:



for(int i = 0; i < buttonList.size(); i++) {
    Button button = buttonList.get(i);
    button.setTag(i);
    button.setText("Question Details");
    button.setOnClickListener(new OnClickListener() {
        public void onClick(View arg0) {
            x = (Integer) arg0.getTag();
            displayDetails();
        }
    });
}

      

+2


source


You cannot refer to a variable in an anonymous function. There are 3 ways to fix this.

1) Use setTag and getTag on the view to tag it with the index number

2) Don't use anonymous class. Make it a private class, defined outside the function (but inside the activity class), and force it to take an index through your constructor.

3) On each click, click the list of buttons and find the index that is equal to that passed in the view.

I think number 2 is the cleanest, but either one will work.

0


source







All Articles