This is a simple implementation of the ViewHolder pattern. I have a cursor containing articles and I want to keep the title in the row tag. At this stage I (try to) keep the actual string and the corresponding textview in the row tag plus the actual string in the textview’ s tag.
class ArticleListCursorAdapter extends SimpleCursorAdapter {
class ViewHolder {
String strTitle = null;
View viewTitle = null;
ViewHolder(View base, String strTitle) {
this.viewTitle = base.findViewById(R.id.textTitle);
this.strTitle = strTitle;
}
@Override
public String toString() {
return new StringBuffer()
.append("strTtitle [").append(strTitle).append("] ")
.append("viewTitle [").append(((TextView)viewTitle).getText()).append("] ")
.append("viewTitle title (tag) [").append(viewTitle.getTag()).append("] ")
.toString();
}
}
public ArticleListCursorAdapter(Context context, int layout, Cursor c, String[] from,
int[] to, int flags) {
super(context, layout, c, from, to, flags);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = super.getView(position, convertView, parent);
ViewHolder holder = null;
// if (convertView != null)
holder = (ViewHolder)row.getTag();
Log.v(TAG, "getView for [" + position + "]. holder ["
+ (holder == null ? "null holder" : holder.toString()) + "]");
Log.v(TAG, "getView other info: rendering view title ["
+ ((TextView)row.findViewById(R.id.textTitle)).getText()
.toString() + "]");
if (holder == null) {
Cursor cursor = (Cursor)ArticleListCursorAdapter.this.getItem(position);
final String strTitle = cursor.getString(cursor.getColumnIndex(ArticleData.C_TITLE));
holder = new ViewHolder(row, strTitle);
Log.v(TAG, "getView setup onclick for pos [" + position + "] and title ["+ strTitle + "]");
holder.viewTitle.setTag(strTitle);
row.setTag(holder);
}
return row;
}
}
When I run it I expect to receive a bunch of:
getView for [x]. holder [strTitle [7777777] viewTitle [7777777] viewTitle title (tag) [7777777] ]
getView other info: rendering view title [7777777]
if the article title is 7777777. What I actually receive is a weird mix of values
(4 items visible at a time, first page)
getView for [0]. holder [null holder]
getView other info: rendering view title [1111111]
getView setup onclick for pos [0] and title [1111111]
getView for [1]. holder [null holder]
getView other info: rendering view title [2222222]
getView setup onclick for pos [1] and title [2222222]
getView for [2]. holder [null holder]
getView other info: rendering view title [3333333]
getView setup onclick for pos [2] and title [3333333]
getView for [3]. holder [null holder]
getView other info: rendering view title [4444444]
getView setup onclick for pos [3] and title [4444444]
(page down)
getView for [4]. holder [strTitle [1111111] viewTitle [5555555] viewTitle title (tag) [1111111] ]
getView other info: rendering view title [5555555]
getView for [5]. holder [null holder]
getView other info: rendering view title [6666666]
getView setup onclick for pos [5] and title [6666666]
one more down
getView for [6]. holder [strTitle [2222222] viewTitle [7777777] viewTitle title (tag) [2222222] ]
getView other info: rendering view title [7777777]
getView for [7]. holder [strTitle [3333333] viewTitle [8888888] viewTitle title (tag) [3333333] ]
getView other info: rendering view title [8888888]
getView for [8]. holder [strTitle [4444444] viewTitle [9999999] viewTitle title (tag) [4444444] ]
getView other info: rendering view title [9999999]
(scroll up)
getView for [4]. holder [strTitle [4444444] viewTitle [5555555] viewTitle title (tag) [4444444] ]
getView other info: rendering view title [5555555]
getView for [3]. holder [strTitle [3333333] viewTitle [4444444] viewTitle title (tag) [3333333] ]
getView other info: rendering view title [4444444]
(scroll up again)
getView for [2]. holder [strTitle [1111111] viewTitle [3333333] viewTitle title (tag) [1111111] ]
getView other info: rendering view title [3333333]
getView for [1]. holder [strTitle [2222222] viewTitle [2222222] viewTitle title (tag) [2222222] ]
getView other info: rendering view title [2222222]
getView for [0]. holder [strTitle [6666666] viewTitle [1111111] viewTitle title (tag) [6666666] ]
getView other info: rendering view title [1111111]
1/ Why the string part of the ViewHolder keeps changing?
2/ How can the viewTitle TextView alters its tag contents (the 5555555 viewTitle above has 1111111 in it’s tag one time and 444444 the other)
Adapters recycle views to save resources. Imagine a ListView with 1,000 rows: it would be slow and unnecessary to create a unique View for each row. So the adapter creates only enough Views that are visible, plus a few more for scrolling; and simply reuses these recyclable rows over and over.
What you are seeing is that you have set one ViewHolder to each recyclable row (0-3) but when you scroll the TextViews change to reflect the list data. This is why recyclable row #0 has the
strTitlevalue for row #0 but the TextView for the actual row #4.Addition from Comments
Yes you could, but just like having 1000 unique rows, this is excessive. I want to propose a more efficient way to do what you want:
I moved everything from getView() to bindView(). In a CursorAdapter the getView() method calls bindView(), this is where all of the data relevant to the Cursor happens. Since most of your code is Cursor related moving the code here saves the extra work of finding the Cursor in getView(). You can still override getView() and call on the ViewHolder, but in this case it is no longer necessary.
I also pulled out the search for the column index to stop the extra work there. (Understand this isn’t perfect, calling a method like adapter.changeCursor() might rearrange the column indices so you would need to re-check the index. But this is a basic example, I didn’t want to over think this.)
Now if you add your LogCat statements back in, you’ll notice that as you scroll up and down the row will retain matching 111111 with 111111 and 444444 with 444444 pairs. Hope that clears some things up!