【问题标题】:How to update Android ListView with dynamic data in real time?如何使用动态数据实时更新Android ListView?
【发布时间】:2011-08-16 07:49:31
【问题描述】:

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

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

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

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

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

【问题讨论】:

    标签: android performance listview background redraw


    【解决方案1】:

    我曾经使用notifyDataSetChanged() 实现了类似下面代码的过滤器并且没有任何问题。

    我还手动修改了移动列表的视图。两者都运作良好。在某些情况下,我不喜欢手动修改数据,因为它更快并且不会影响整个列表。

    无论如何,视图在需要出现在屏幕上时会在旅途中创建,并在离开屏幕时被删除,因此如果您修改用于创建视图的数据,如果用户滚动 ListView 并且视图得到理论上,一旦它们再次进入屏幕,视图将使用新数据创建。

    我建议您尝试以下代码以了解 notifyDataSetChanged() 的工作原理并确定它是否适合您。

    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>
    

    希望这会有所帮助。

    【讨论】:

    • 我忘记了什么。在这种情况下,我使用了固定图片,但在我的程序中,我下载了图片并显示了加载图片,直到下载了图片,然后我通过定位视图手动修改它。它就像我期望的那样工作(与 700 多个朋友的 followFriends 数组)。我不使用notifyDataSetChanged() 来更新图片,因为它不是必需的,并且通过手动操作程序不会变慢。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      我对@9​​87654321@ 进行了试验,如果您有实时数据并且希望ListView 以更好的性能更新,则基本上必须手动更新ListView 单元格而不调用notifyDataSetChanged()

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

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

      我实施了一个方案,如果我的数据集大小从一个更新更改为下一个更新,我调用notifyDataSetChanged()。如果数据集大小从一次更新到下一次更新保持不变(这样ListView 中的单元格数量与之前需要重绘数据时相同),那么我将遍历ListView.getFirstVisiblePosition():@ 987654331@,只更新可见单元格。

      【讨论】:

      • ListView 不为其适配器中的每个对象保存一个视图。视图被重复使用,因此可见视图是唯一的视图。所以你的实现没有优化任何东西。
      • 您应该查看适配器的 ViewHolder 模式
      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-11-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多