ulmaxy ulmaxy - 3 months ago 15
Android Question

getVIew has its convertView == null for already existed item when I add new item to listView

I'm trying to add items in

ListView
dynamicly. When I do that for the first time (so list is empty),
getView()
works pretty fine, convertView is
null
as it supposed to be, so I can use a
ViewHolder
to store references to item's views and bind it to the view through setTag(). But when I add a second item to the list with the data and call
notifyOnDataSetChanged()
really weird behavior has been noticed.

Basically,
notifyOnDataSetChanged()
makes all the visible items to be redrawn, and it starts from the first visible item. The problem is that when the first item (which was already drawn before) is being redrawn, convertView == null, so I need to inflate new view for it and use
findViewById()
to find all the references that I already have found before and stored inside ViewHolder. For the second item I receive convertView that has references to the first one, so I need to find it for the second item. What I want is to get the view that was created before (and has all references inside its ViewHolder) for the first item, and create new one for the second item, i.e. have convertView == null when drawing the item I just added. Is there any way to do that?

EDIT 1: Add new item:

alarmClocksList.add(new AlarmClock(time, songTitle, weekDaysPanelVisible, weekDaysOn));
alarmsArrayAdapter.notifyDataSetChanged();


ArrayAdapter:

package com.example;

import android.content.Context;
import android.content.Intent;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.RelativeLayout;
import android.widget.TextView;

import java.util.ArrayList;

public class AlarmsArrayAdapter extends ArrayAdapter {

ArrayList<AlarmClock> alarmClockArrayList;
private Context context;

static class ViewHolder {
TextView time;
TextView songTitle;
TextView nextRingTime;
CheckBox repeatCheckBox;
RelativeLayout repeatPanel;
WeekDayCheckBox monday;
WeekDayCheckBox tuesday;
WeekDayCheckBox wednesday;
WeekDayCheckBox thursday;
WeekDayCheckBox friday;
WeekDayCheckBox saturday;
WeekDayCheckBox sunday;
}

public AlarmsArrayAdapter(Context context, ArrayList<AlarmClock> alarmClocks) {
super(context, -1, alarmClocks);
alarmClockArrayList = alarmClocks;
this.context = context;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.alarm_clock_item, parent, false);

holder = new ViewHolder();

holder.time = (TextView) convertView.findViewById(R.id.alarmClockTime);
holder.songTitle = (TextView) convertView.findViewById(R.id.alarmClockSong);
holder.nextRingTime = (TextView) convertView.findViewById(R.id.nextRindDay);
holder.repeatCheckBox = (CheckBox) convertView.findViewById(R.id.repeatCheckBox);

holder.repeatPanel = (RelativeLayout) convertView.findViewById(R.id.repeatPanel);

holder.monday = (WeekDayCheckBox) convertView.findViewById(R.id.MO);
holder.tuesday = (WeekDayCheckBox) convertView.findViewById(R.id.TU);
holder.wednesday = (WeekDayCheckBox) convertView.findViewById(R.id.WE);
holder.thursday = (WeekDayCheckBox) convertView.findViewById(R.id.TH);
holder.friday = (WeekDayCheckBox) convertView.findViewById(R.id.FR);
holder.saturday = (WeekDayCheckBox) convertView.findViewById(R.id.SA);
holder.sunday = (WeekDayCheckBox) convertView.findViewById(R.id.SU);

holder.songTitle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});

holder.time.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(context, TimePickerActivity.class);
((FragmentActivity) context).startActivityForResult(intent, MainActivity.TIME_PICK_REQUEST);
}
});

holder.repeatCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (!isChecked) {
holder.repeatPanel.setVisibility(View.GONE);
} else {
holder.repeatPanel.setVisibility(View.VISIBLE);
}
}
});
convertView.setTag(holder);
} else
holder = (ViewHolder) convertView.getTag();

AlarmClock alarmClock = alarmClockArrayList.get(position);
holder.time.setText(alarmClock.time);
holder.songTitle.setText(alarmClock.songTitle);
holder.nextRingTime.setText("No data");
return convertView;
}
}

Answer

Alright, I finally figured it out! What I needed to do is to set height and width of the ListView to match_parent. If you use wrap_content it forces ListView to invoke getView() at least 2 times for each item, which was the main cause of my problem.