У меня есть ListView, который использует BaseAdapter, а ListView расположен в ViewGroup. Когда я вызываю notifyDataSetChanged() через runOnUiThread() из другого потока, ListView не обновляется. Только когда я касаюсь и перетаскиваю, другими словами, пытаюсь прокрутить, ListView обновляется.
Я понимаю, что у многих людей возникают проблемы, когда они вызывают notifyDataSetChanged() из другого потока, кроме потока пользовательского интерфейса, но я этого не делаю, и у меня все еще есть проблема.
Приведенный ниже код кажется минимальным для воспроизведения проблемы.
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;
}
}
}
Что я заметил, так это то, что getView() в адаптере вызывается, затем я пытаюсь прокрутить, а не раньше, и появляются три строки, поэтому, очевидно, адаптер обновлен.
Итак, чем отличается вызов через runOnUiThread()?
Если я перейду на:
public void UpdateStringList() {StringViewGroup.post(new Runnable() {public void run()
{
StringViewGroup.UpdateStringList();
}});}
и попробуйте позвонить:
UpdateStringList();
из основного потока я получаю ту же проблему. Я даже пытался вызвать invalidate() и requestLayout() в списке, но безрезультатно (вам в любом случае не нужно этого делать, а просто убедиться)
Может быть, мне нужно что-то сделать в ViewGroup, что я пропустил?