14

我有一个后台线程加载数据,我想在 Android ListView 中显示。数据变化非常频繁(即每秒1-2 次)。有时数据集中的行数也会发生变化(但肯定不会像单元格中的数据变化那样频繁)。

据我所知,有两种方法可以更新单元格中的数据:

  1. 让后台线程通知 UI 线程新数据已准备好,然后 UI 线程可以调用 BaseAdapter.notifyDataSetChanged()。但是,我在不止一个地方读到过,如果经常调用该方法,它会很慢,因为 ListView 必须重组其所有子视图视图。

  2. 如果数据集计数没有更改,我可能会找到与更改的数据关联的所有可见 ListView 单元格,并手动更新值而无需调用 notifyDataSetChanged()。这可能会起作用,但我认为不幸的是,当我通知它时,列表适配器应该为我处理更新通知和机制时,我必须手动更新视图。如果数据集计数随时间变化(即不仅 ListView 的每个单元格内的数据发生变化,而且 ListView 中的单元格总数可以根据提供实时数据的后台线程增加或缩小,此方法也将不起作用)。

我当然会感谢其他实现此场景的人的想法,以及他们如何优化代码的简单性,最重要的是性能。

4

4 回答 4

7

我尝试了,如果您有实时数据并且希望以更好的性能更新 ListView,您基本上必须ListView手动更新单元格而无需调用。notifyDataSetChanged()ListView

notifyDataSetChanged()如果您经常调用它(即每秒一次),则会导致ListView重建其整个层次结构的速度非常慢。View

注意(2014 年)。如果您使用带有 ViewHolder 模式的普通现代 ListView,则此方法不适用。您只需调用“notifyDataSetChanged”即可。它非常高效,因为 Android 知道只更新屏幕上的单元格。

我实施了一个方案,如果我的数据集大小从一个更新更改为下一个更新,我调用notifyDataSetChanged(). 如果数据集大小从一次更新到下一次保持不变(这样ListView当需要重绘数据时,单元格的数量与以前相同),那么我迭代ListView.getFirstVisiblePosition():getLastVisiblePosition()并仅更新可见单元格.

于 2011-05-09T22:43:05.673 回答
2

I once implemented a filter like the code beolow using notifyDataSetChanged() and had no problems with it.

I've also modified the views of a List on the go manually. Both have worked well. In some case I prefear to modify the data manually because its faster and because itdoesn't affect the whole list.

Anyway, views are created on the go when they need to apear on the screen and are deleted when they leave the screen, so if you modify the data used to create the views, if the user scrolls the ListView and the views get out of the screen, in theory, the views will be created with the new data once they come again into the screen.

I would recommend you to try the code below to understand how does notifyDataSetChanged() work and decide if it works for you.

public class ListText extends Activity {


    private ListView lv1;
    private Followed followedFriends[];
    ListView lst;
    EditText edt;
    FollowedFilterableAdapter arrad;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        lv1=(ListView)findViewById(R.id.listView1);
        edt = (EditText) findViewById(R.id.editText1);

        followedFriends = new Followed[10];
        followedFriends[0] = new Followed("Alan Walder", "0123456789", "1");
        followedFriends[1] = new Followed("Alberto Levi", "123456789", "1");
        followedFriends[2] = new Followed("David Rodan", "23456789", "1");
        followedFriends[3] = new Followed("David Stern", "3456789", "1");
        followedFriends[4] = new Followed("Elias Jawa", "456789", "1");
        followedFriends[5] = new Followed("Elian Moreno", "56789", "1");
        followedFriends[6] = new Followed("Jonathan Rodan", "6789", "1");
        followedFriends[7] = new Followed("Klara Rodan", "789", "1");
        followedFriends[8] = new Followed("Willy Rosales", "89", "1");
        followedFriends[9] = new Followed("ZZZ ZZZ", "9", "1");


        arrad =  new FollowedFilterableAdapter(followedFriends);
        lv1.setAdapter(arrad);

        addTextChangeList();
    }

    private void addTextChangeList()
    {
        edt.addTextChangedListener(new TextWatcher()
        {


            public void onTextChanged( CharSequence arg0, int arg1, int arg2, int arg3)
            {
                // TODO Auto-generated method stub

            }

            public void beforeTextChanged( CharSequence arg0, int arg1, int arg2, int arg3)
            {
                // TODO Auto-generated method stub

            }



            public void afterTextChanged( Editable arg0)
            {
                // TODO Auto-generated method stub
                ListText.this.arrad.getFilter().filter(arg0);
            }
        });

    }


    ///////////////////////////////////Internal classes ////////////////////////

    private class Followed
    {
        private String _name;
        private String _id;
        private boolean _followed;
        private String _picToDelete = "http://images.wikia.com/tibia/en/images/7/72/Skeleton.gif";

        private Followed(String name, String id, String followed)
        {
            this._name = name;
            this._id = id;
            this._followed = followed.equals("1");
        }

        public String toString(){return _name;}
        public String getName(){return _name;}
        public String getId(){return _id;}
        public boolean idFollowed(){return _followed;}
        public String getURL(){return _picToDelete;}
    }

    /////////////////////////////////////////Adapter//////////////////////////////////

    private class FollowedFilterableAdapter extends BaseAdapter implements Filterable
    {
        /**
         * Lock used to modify the content of {@link #mObjects}. Any write operation
         * performed on the array should be synchronized on this lock. This lock is also
         * used by the filter (see {@link #getFilter()} to make a synchronized copy of
         * the original array of data.
         */
        private final Object _Lock = new Object();

        /*/**
         * Indicates whether or not {@link #notifyDataSetChanged()} must be called whenever
         * {@link #mObjects} is modified.
         */
        //private boolean _NotifyOnChange = true;

        private List<Followed> _Objects;

        private ArrayList<Followed> _followedFriends;
        private ArrayFilter _Filter;

        public FollowedFilterableAdapter(Followed[] followedFriends)
        {
            _Objects = Arrays.asList(followedFriends);
        }

        public int getCount() {
            return _Objects.size();
        }

        public Followed getItem(int position) {
            return _Objects.get(position);
        }

        public long getItemId(int position) {
            return position;
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            int px = 2;

            //Creating the CategoryRow that represents the row
            LinearLayout lstItem = new LinearLayout(ListText.this);
            lstItem.setLayoutParams(new ListView.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT,1));
            lstItem.setOrientation(LinearLayout.HORIZONTAL);
            //lstItem.setPadding(px,px,px,px);
            lstItem.setGravity(Gravity.CENTER_VERTICAL);
            /*<LinearLayout android:layout_width="fill_parent"
                            android:layout_height="wrap_content" android:orientation="horizontal"
                            android:padding="2dp" android:gravity="center_vertical">*/

            //Adding the Image
            ImageView icon = new ImageView(ListText.this);
            icon.setLayoutParams(new LayoutParams(50,50));
            icon.setImageResource(R.drawable.icon);
            icon.setScaleType(ScaleType.CENTER_CROP);
            //icon.setImage(tag.getId());
            /*<ImageView android:layout_width="50dp" android:id="@+id/iconFollList"
                                android:src="@drawable/icon" android:layout_height="40dp"></ImageView>*/

            //Adding the Linear Layout for the text
            RelativeLayout lstTextx = new RelativeLayout(ListText.this);
            lstTextx.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT,1));
            lstTextx.setGravity(Gravity.CENTER_VERTICAL);
            lstTextx.setPadding(5, px, px, px);
            /*<LinearLayout android:layout_width="fill_parent"
                                android:layout_height="wrap_content" android:orientation="vertical"
                                android:padding="2dp" android:paddingLeft="5dp">*/

            //Adding the Name of the person who commented
            TextView txtCommenter = new TextView(ListText.this);
            txtCommenter.setLayoutParams(
                    new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
                    LayoutParams.WRAP_CONTENT));
            txtCommenter.setTextSize(10);
            txtCommenter.setTypeface(Typeface.create("Sans Serif", Typeface.BOLD));
            txtCommenter.setText(_Objects.get(position).getName());
            /*<TextView android:text="TextView" android:id="@+id/FollListCategory"
                                    android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>*/


            ImageView btnFollowed = new ImageView(ListText.this);
            RelativeLayout.LayoutParams params = 
                new RelativeLayout.LayoutParams(80,30);
            params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
            btnFollowed.setLayoutParams(params);
            btnFollowed.setImageResource(R.drawable.icon);
            btnFollowed.setScaleType(ScaleType.FIT_XY);

            //Arming the View
            lstItem.addView(icon, 0);
            lstTextx.addView(txtCommenter, 0);
            lstTextx.addView(btnFollowed,1);
            lstItem.addView(lstTextx,1);

            return lstItem;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void notifyDataSetChanged() {
            super.notifyDataSetChanged();
            //_NotifyOnChange = true;
        }

        /*public void setNotifyOnChange(boolean notifyOnChange) {
            _NotifyOnChange = notifyOnChange;
        }*/

        public Filter getFilter() {
            if (_Filter == null) {
                _Filter = new ArrayFilter();
            }
            return _Filter;
        }

        /////////////////////Second Level Internal classes //////////////////////////

        private class ArrayFilter extends Filter {
            @Override
            protected FilterResults performFiltering(CharSequence prefix) {
                FilterResults results = new FilterResults();

                if (_followedFriends == null) {
                    synchronized (_Lock) {
                        _followedFriends = new ArrayList<Followed>(_Objects);
                    }
                }

                if (prefix == null || prefix.length() == 0) {
                    synchronized (_Lock) {
                        ArrayList<Followed> list = new ArrayList<Followed>(_followedFriends);
                        results.values = list;
                        results.count = list.size();
                    }
                } else {
                    String prefixString = prefix.toString().toLowerCase();

                    final ArrayList<Followed> values = _followedFriends;
                    final int count = values.size();

                    final ArrayList<Followed> newValues = new ArrayList<Followed>(count);

                    for (int i = 0; i < count; i++) {
                        final Followed value = values.get(i);
                        final String valueText = value.toString().toLowerCase();

                        // First match against the whole, non-splitted value
                        if (valueText.startsWith(prefixString)) {
                            newValues.add(value);
                        } else {
                            final String[] words = valueText.split(" ");
                            final int wordCount = words.length;

                            for (int k = 0; k < wordCount; k++) {
                                if (words[k].startsWith(prefixString)) {
                                    newValues.add(value);
                                    break;
                                }
                            }
                        }
                    }

                    results.values = newValues;
                    results.count = newValues.size();
                }

                return results;
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                //no inspection unchecked
                _Objects = (List<Followed>) results.values;
                if (results.count > 0) {
                    notifyDataSetChanged();
                } else {
                    notifyDataSetInvalidated();
                }
            }
        }
    }

xml

    <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <EditText android:text="" android:layout_height="wrap_content"
    android:id="@+id/editText1" android:layout_width="match_parent"></EditText>
    <ListView android:id="@+id/listView1" android:layout_height="fill_parent"
    android:layout_width="match_parent" android:layout_weight="1"></ListView>

</LinearLayout>

Hope this helps.

于 2011-05-01T05:00:22.863 回答
1

也许你可以在 UI 线程上设置一个 Handler。您需要创建一个实现 Runnable 的类。将 ArrayList 传递给此类。在 run() 方法中,使用 ArrayList 作为参数创建适配器,然后在 ListView 上执行 setAapter。而已。你完成了。要启动您的处理程序:就从您的工作线程: handler.Post(new MyUpdateToUI() ); 而已。我希望它对你来说足够有效吗?

于 2011-05-01T07:24:22.243 回答
0

我遇到了一个非常相似的情况,我将 List View 的 textviews 存储在一个 Hashmap 中,其中每个 textview 都有一个标签来识别它。当流数据到达时,我所做的只是在找到正确的数据后在 textview 上发布一个可运行文件,并立即更新它们。然而,尽管更新是即时的,但当我每秒更新大约 30 个文本视图时,UI 面临拖累并且变得迟缓。我的 Listview 在 ViewPager 的一个片段中,当流媒体打开时,UI 线程似乎都被阻塞和阻塞了。因此,就用户体验和 UI 线程的性能而言,我强烈建议不要手动更新 Listview 中的 Textview

于 2018-07-02T20:42:28.423 回答