I have a ListView that is using a BaseAdapter and the ListView is positioned in a ViewGroup. When I call notifyDataSetChanged() via runOnUiThread() from another thread the ListView is not updating itself. Only when I touch and drag, in other words try to scroll, the ListView updates.
I get that a lot of people having problem when they call notifyDataSetChanged() from another thread than the UI thread but I am not doing that and still having problem.
The code below seem to be a bare minimum to reproduce the issue.
public class MyMainActivity extends Activity
{
public MyStringViewGroup StringViewGroup = null;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
StringViewGroup = new MyStringViewGroup(this);
setContentView(StringViewGroup);
// This is the thread I'll call UpdateStringList() from
new OtherThread(this).start();
//UpdateStringList(); // If I call it from here, everything works
}
// This should post the runnable on the UI-thread so StringViewGroup.UpdateStringList() is called from the right thread
public void UpdateStringList() {runOnUiThread(new Runnable() {public void run()
{
StringViewGroup.UpdateStringList();
}});}
}
class OtherThread extends Thread
{
MyMainActivity Main = null;
OtherThread(MyMainActivity main)
{
Main = main;
}
@Override public void run()
{
// Wait 2 seconds
try {sleep(2000);} catch (InterruptedException e) {}
// Call the funktion that add the three rows
Main.UpdateStringList(); // If i call it from here, It's not working
}
}
class MyStringViewGroup extends ViewGroup
{
ListView StreamListView = null;
MyStringListAdapter StringListAdapter = null;
ArrayList<String> StringArray = new ArrayList<String>();
public MyStringViewGroup(MyMainActivity context)
{
super(context);
StreamListView = new ListView(context);
StreamListView.setId(android.R.id.list);
addView(StreamListView);
StringListAdapter = new MyStringListAdapter(StringArray);
StreamListView.setAdapter(StringListAdapter);
}
public void UpdateStringList()
{
StringArray.add("Row 1");
StringArray.add("Row 2");
StringArray.add("Row 3");
StringListAdapter.notifyDataSetChanged();
}
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom)
{
StreamListView.layout(left, top, right, bottom);
}
// An ordinary Adapter, nothing special here (I hope)
public class MyStringListAdapter extends BaseAdapter
{
ArrayList<String> StringArray = null;
public MyStringListAdapter(ArrayList<String> stringArray) {StringArray = stringArray;}
@Override public int getCount() {return StringArray.size();}
@Override public String getItem(int position) {return StringArray.get(position);}
@Override public long getItemId(int position) {return position;}
@Override public View getView(int position, View convertView, ViewGroup parent)
{
final TextView textView;
if (convertView == null)
{
textView = new TextView(MyStringViewGroup.this.getContext());
textView.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
}
else
textView = (TextView)convertView;
textView.setText(StringArray.get(position));
return textView;
}
}
}
What i have noticed is that getView() in the adapter is called then i try to scroll and not before and the three rows appears so apparently the adaptor is updated.
So, what is different when calling via runOnUiThread()?
If i change to:
public void UpdateStringList() {StringViewGroup.post(new Runnable() {public void run()
{
StringViewGroup.UpdateStringList();
}});}
and try to call:
UpdateStringList();
from the main thread, I get the same issue. I've even tried to call invalidate() and requestLayout() on the listview with no result (you should not need to do that anyway but just to make sure)
Can it be something I need to do in the ViewGroup that I have missed?
Ok, I just figured out that you absolutely must call measure() on the ListView in the onLayout-call, like:
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom)
{
StreamListView.measure(...);
StreamListView.layout(left, top, right, bottom);
}
It's still really strange that the bug only is happening when notifyDataSetChanged() is called from another thread via runOnUiThread(new Runnable()), but I guess measure() is mandatory.