0

I'm pretty new to Android, and I'm currently having some problem in using custom list adapter.

Basically, I want to make a list of question, with different type for each question (radio, checkbox, text, numeric). When the listview is generated, it displayed fine. But when I scroll down, the item from the top gets regenerated in the bottom.

My code is like this :

public View getView(int position, View convertView, ViewGroup parent) {

  View someView = convertView;

  //get question type
  String type = questions.get(position)[2];

  //declare viewholder
  final ViewHolder holder;
  Log.i("Position", Integer.toString(position));

  if (someView == null) {
    Log.i("convertView", "null");
    holder = new ViewHolder();

    LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    someView = inflater.inflate(R.layout.list_survey, parent, false);

    TextView textView = (TextView) someView.findViewById(R.id.surveyQuestion);
    textView.setText(questions.get(position)[1]);

    LinearLayout optionContainer = (LinearLayout) someView.findViewById(R.id.surveyOption);

    int size = options.get(position).size();

    //radio button
    if (type.equals("radio")) {

        RadioGroup group = new RadioGroup(this.context);
        RelativeLayout.LayoutParams groupParam = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
        optionContainer.addView(group, groupParam);
        RadioButton radio = null;

        ArrayList<RadioButton> radios = new ArrayList<RadioButton>();

        for (int i=0; i<size; i++){
            radio = new RadioButton(this.context);
            radio.setId(i+1);
            radio.setText(options.get(position).get(i)[1]);
            group.addView(radio);
            radios.add(radio);
        }      
        group.setOnCheckedChangeListener(new OnCheckedChangeListener() 
        {
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                //Model element = (Model)group;
            }
        });

        holder.radiogroup = group;
        holder.radiobuttons = radios;

    //checkbox 
    } else if (type.equals("check")) {
        CheckBox checkbox = null;

        ArrayList<CheckBox> checks = new ArrayList<CheckBox>();

        for (int i=0; i<size; i++){
            checkbox = new CheckBox(this.context);
            checkbox.setId(i+1);
            checkbox.setText(options.get(position).get(i)[1]);
            optionContainer.addView(checkbox);
            checks.add(checkbox);
        }

        holder.checkboxes = checks;

    //textfield
    } else if (type.equals("txt")) {
        EditText field = null;

        ArrayList<EditText> fields = new ArrayList<EditText>();

        for (int i=0; i<size; i++){
            field = new EditText(this.context);
            field.setId(i+1);
            field.setHint(options.get(position).get(i)[1]);
            optionContainer.addView(field);
            fields.add(field);
        }

        holder.edittext = fields;
    //numeric
    } else if (type.equals("num")) {
        EditText field = null;

        ArrayList<EditText> fields = new ArrayList<EditText>();

        for (int i=0; i<size; i++){
            field = new EditText(this.context);
            field.setId(i+1);
            field.setHint(options.get(position).get(i)[1]);
            field.setInputType(InputType.TYPE_CLASS_NUMBER);
            optionContainer.addView(field);
            fields.add(field);
        }

        holder.edittext = fields;
    }

    someView.setTag(holder);

  } else {
    Log.i("convertView", "not null");
    holder = (ViewHolder) someView.getTag();
  }


    return someView;
}

The viewHolder class is here :

//viewHolder
static class ViewHolder {
    ArrayList<CheckBox> checkboxes;
    ArrayList<EditText> edittext;
    RadioGroup radiogroup;
    ArrayList<RadioButton> radiobuttons;    
}

What am I doing wrong?

I really appreciate your help. I've searched some other solutions but I can't get it work on my code.

Thanks

alfa
  • 3
  • 1
  • **Just** the first item? Could you paste how are you creating your custom `ArrayAdapter` instance? – nKn Feb 10 '14 at 22:13
  • Ah sorry, I only noticed the first occurrence, but then I saw your post and recheck it. Apparently every time it's scrolled, the item that got out of the screen gets recalled and replaces the next item that should be on the list. – alfa Feb 11 '14 at 03:39
  • Then @LukaCiko's answer is correct. You're simply managing the tag, but you're not updating your `View`s inside your `ViewHolder`. – nKn Feb 11 '14 at 07:14

1 Answers1

3

This happened because you are using the convertView and ViewHolder incorrectly. When you scroll down, the views will be recycled and the converView will not be null. The ViewHolder pattern is usually used this way:

public View getView(int position, View convertView, ViewGroup parent) {
    if(convertView == null) {
        inflate the view
        create a ViewHolder and store the necessary views in it
        use setTag to store the ViewHolder in the convertView
    }
    else {
        use getTag to get the ViewHolder
    }
    update the views // <- you are not doing this when convertView != null
}

The reason that convertView/ViewHolder pattern is used is that views in a list usually have the same layout but different data shown in them. In essence, the convertView (when not null) is a view that was previously used in the list and must be converted to show correct data for this position. By reusing it you avoid the expensive layout inflation. The ViewHolder is a further optimization with which you avoid the also expensive findViewById calls.

LukaCiko
  • 4,437
  • 2
  • 26
  • 30
  • `expensive findView calls` How expensive is finding a `View` in a `ViewGroup` by its id? It's a simple iteration ... – gunar Feb 10 '14 at 22:42
  • Well it would seem so, but it has to traverse the `ViewGroup` hierarchy recursively. From my experience the "`ViewHolder` optimization" has a noticeable effect, at least when the layout isn't very basic. – LukaCiko Feb 10 '14 at 22:47
  • I understand the basic concept from your explanation, but I'm still unclear about what should I do to make it work. What does update the views mean? Should I take the code that generates the view out of the if statement? If so, what should I put inside the if statement to save the state to viewHolder? – alfa Feb 11 '14 at 07:49
  • Most of your code should be in what I called `update the views`. In the body of the if statement there should be only what is the same for all the views (probably not much, if anything) + `inflate`, `findViewById` and `setTag`. – LukaCiko Feb 11 '14 at 09:58