代码之家  ›  专栏  ›  技术社区  ›  Floaf

ListView上的notifyDataSetChanged()无法使用runOnUiThread(new Runnable())

  •  0
  • Floaf  · 技术社区  · 10 年前

    我有一个使用BaseAdapter的ListView,ListView位于ViewGroup中。 当我从另一个线程通过runOnUiThread()调用notifyDataSetChanged()时,ListView本身没有更新。只有当我触摸并拖动,换句话说,尝试滚动时,ListView才会更新。

    我发现很多人在从UI线程以外的另一个线程调用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();
    

    从主线程,我得到了相同的问题。 我甚至尝试在listview上调用invalidate()和requestLayout(),但没有结果(无论如何,您不需要这样做,只是为了确保)

    这是我需要在ViewGroup中做的事情吗?

    1 回复  |  直到 10 年前
        1
  •  0
  •   Floaf    10 年前

    好的,我刚刚发现您必须在onLayout调用中调用ListView上的measure(),比如:

    @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom)
    {
        StreamListView.measure(...);
        StreamListView.layout(left, top, right, bottom);
    }
    

    仍然很奇怪的是,只有当通过runOnUiThread(new Runnable())从另一个线程调用notifyDataSetChanged()时,才会出现错误,但我猜measure()是必需的。