I have a listview whose adaper extends CursorAdapter class. I can fetch data from cursor and show the information about tv program in listview succesfully. I want to mark the tv program that in active situation by looking start and end time of program in the list. So I want to change background color of active one. Everything is run corretly but when I scroll down the list, some other rows marked too. I have researched some documents about listview recycling but I couldnt understand. Here is my code:
My adapter class :
public class StreamListAdapter extends CursorAdapter {
//............
public void bindView(View view, Context context, Cursor cursor) {
// TODO Auto-generated method stub
ViewHolder holder = (ViewHolder)view.getTag();
Typeface fontStyle = Typeface.createFromAsset(context.getAssets(),"fonts/CenturyGothicItalic.ttf");
holder.tVName.setTypeface(fontStyle);
holder.tVBrief.setTypeface(fontStyle);
holder.tVClock.setTypeface(fontStyle);
holder.tVStat.setTypeface(fontStyle);
holder.tVGenre.setTypeface(fontStyle);
RelativeLayout rL = (RelativeLayout)view.findViewById(R.id.stream_list_item);
String title = cursor.getString(cursor.getColumnIndex("ProgramName"));
String brief = cursor.getString(cursor.getColumnIndex("ProgramBrief"));
String clock = cursor.getString(cursor.getColumnIndex("ProgramClock"));
String genre = cursor.getString(cursor.getColumnIndex("ProgramGenre"));
String stat = cursor.getString(cursor.getColumnIndex("ProgramStat"));
String imageURL = cursor.getString(cursor.getColumnIndex("ProgramImageUrl"));
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm");
Date dt = new Date();
String strValue = timeFormat.format(dt);
String prgStartTime = clock.substring(6, 11);
String prgEndTime = clock.substring(14);
try {
Date prgClockStart = timeFormat.parse(prgStartTime);
Date systemClock = timeFormat.parse(strValue);
Date prgClockEnd = timeFormat.parse(prgEndClock);
if(((systemClock.compareTo(prgClockStart) > 0) || (systemClock.compareTo(prgClockStart) == 0)) & (systemClock.compareTo(prgClockEnd) < 0)){
rL.setBackground(context.getResources().getDrawable(R.drawable.activegradient));
}
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
holder.tVName.setText(title);
holder.tVBrief.setText(brief);
holder.tVClock.setText(clock);
holder.tVStat.setText(stat);
holder.tVGenre.setText(genre);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// TODO Auto-generated method stub
View row = cursorInflater.inflate(R.layout.stream_list_item, parent, false);
ViewHolder holder = new ViewHolder(row);
row.setTag(holder);
return row;
}
}
The bit of code that says:
if(((systemClock.compareTo(prgClockStart) > 0) || (systemClock.compareTo(prgClockStart) == 0)) & (systemClock.compareTo(prgClockEnd) < 0)){
rL.setBackground(context.getResources().getDrawable(R.drawable.activegradient));
}
needs to have an else block like
else{
rL.setBackground( // tranparent or non-active color )
}
This is because the views are recycled. Say view "1" scrolls of the top of the screen, if you keep scrolling that view will eventually be recycled and appear at the bottom ready to be rebound.
In a cursor adapter, you have one method for creating views and one for binding them. If you put some logs in your code, you'll see that the app only creates a few views, but calls bindView many times when it's reusing them all.
This is because it's much more efficient to reuse a view than keep lots of different ones that aren't being used in memory.
To put it simply -- if you have an "if" block in the bindView method, there must always be an else. Never assume your view has started out as a blank slate.