I'm developing an Android app in which I've got some lists. It's more complex than I'll explain here, but the scenario I'm posting behaves unexpectedly as well.
I've been browsing several questions in Stack Overflow but none that I found solves this issue.
So, for the sake of the question I've built this scenario. I've got an Activity with an array of Strings, like so:
public class TestViewlazyloadActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
String[] items = { "lorem", "ipsum", "dolor", "sit", "amet" };
ListView list = (ListView) findViewById(R.id.testListView);
TestAdapter adapter = new TestAdapter(this, R.layout.list_item, items);
list.setAdapter(adapter);
}
}
Being my two layouts this way:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ListView
android:id="@+id/testListView"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</ListView>
</LinearLayout>
And this way:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/testTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
Pretty simple. Then I've implemented an ArrayAdapter class with the ViewHolder idea, which getView() method is as follows:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
String item = this.getItem(position);
if(convertView == null) {
LayoutInflater inflater = ((Activity) this.getContext()).getLayoutInflater();
convertView = inflater.inflate(R.layout.list_item, null);
holder = new ViewHolder();
holder.textView = (TextView) convertView.findViewById(R.id.testTextView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final ViewHolder auxHolder = holder;
LoadingService.capitalizeItemWithDelay(item, new LoadedCallback() {
public void onItemLoaded(String item) {
auxHolder.textView.setText(item);
}
});
return convertView;
}
Being LoadedCallback
an interface with just onItemLoaded
. And LoadingService.capitalizeItemWithDelay()
just starts a Thread which sleeps for a random amount of time (to simulate a lazy load) and calls this callback with the upper case String.
Normal behavior should be that user is viewing the list with 5 blank rows. After some time items begin to spawn in their respective rows, even in a random order given the random sleep timer.
What really happens is that items get loaded in the first row, one by one, and eventually some of them get set in their real place. It's like the auxHolder
keeps the reference in its TextView to the first element, which is weird. I understand it's either because of the view being recycled or because of the auxHolder
being final
.
Any ideas? Thanks in advance.