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
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.