Как обновить Android ListView с динамическими данными в режиме реального времени?

У меня есть данные загрузки фонового потока, которые я хочу отобразить в ListView Android. Данные изменения очень часто (т. е. 1-2 раза в секунду). Иногда количество строк в наборе данных также изменяется (но, конечно, не так часто, как данные в ячейках).

есть два способа обновить данные в ячейках, насколько я могу судить:

  1. имейте фоновый поток уведомить поток пользовательского интерфейса, что новые данные готовы, и поток пользовательского интерфейса может затем вызовите BaseAdapter.notifyDataSetChanged(). Однако я прочитал в нескольких местах, что если этот метод вызывается часто, он будет медленным, потому что ListView должен реструктурировать все свои представления подвидов.

  2. Если количество наборов данных не изменилось, я мог бы найти все видимые ячейки ListView, связанные с измененными данными, и обновить значения вручную без вызова notifyDataSetChanged(). Это, вероятно, сработает, но я думаю, что его к сожалению, мне приходится обновлять представления вручную, когда адаптер списка должен обрабатывать уведомления об обновлении и механизмы для меня, когда я уведомляю его. Этот метод также не будет работать, если количество наборов данных изменяется с течением времени (т. е. не только данные в каждой ячейке ListView меняются, но общее количество ячеек в ListView может расти или сжиматься на основе фонового потока, предоставляющего данные в реальном времени).

Я бы, конечно, оценил мысли от другие, которые реализовали этот сценарий, и как они оптимизировали простоту кода и, самое главное, производительность.

4 ответов


я экспериментировал с ListView, и вы по существу должны обновить ListView ячейки вручную без вызова notifyDataSetChanged() Если у вас есть данные в реальном времени и вы хотите, чтобы ListView для обновления с лучшей производительностью.

notifyDataSetChanged() вызывает ListView перестроить его полностью View иерархия очень медленная, если вы часто ее вызываете (т. е. раз в секунду).

Примечание (2014). Это не применяется, если вы используете обычный современный ListView с Паттерн ViewHolder. Вы просто позвонить notifyDataSetChanged и это все. Это невероятно эффективно, так как Android знает только обновлять ячейки на экране.

я реализовал схему, где, если размер моего набора данных изменился с одного обновления на другое, я вызываю notifyDataSetChanged(). Если размер набора данных оставался постоянным от одного обновления до следующего (например, количество ячеек в ListView то же самое, что и раньше, когда требуется перерисовка данных), затем я повторяю над ListView.getFirstVisiblePosition() : getLastVisiblePosition(), и обновлять только видимые ячейки.


Я когда-то реализовал фильтр, такой как код beolow, используя 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>

надеюсь, что это помогает.


возможно, вы можете установить обработчик в потоке пользовательского интерфейса. Вам нужно создать класс, который реализует Runnable. Передайте ArrayList этому классу. В методе run () создайте адаптер с ArrayList в качестве параметра, затем выполните setAapter в ListView. Вот и все. Вы закончили. Чтобы запустить обработчик: только для этого из вашего рабочего потока: обработчик.Пост(Нью-MyUpdateToUI() ); Вот и все. Я надеюсь, что это достаточно эффективно для вас?


Я столкнулся с очень похожей ситуацией, когда я сохранил textviews представления списка в Hashmap, где каждый textview имел тег для его идентификации. Когда потоковые данные прибыли, все, что я сделал, это разместил runnable на textview после поиска правильного и получил их обновление в кратчайшие сроки. Однако, хотя обновления были мгновенными, пользовательский интерфейс столкнулся с перетаскиванием и пошел вяло, поскольку я обновлял около 30 textviews в секунду. У меня был Listview в фрагменте внутри ViewPager, и это казалось UI поток был забит и заблокирован, когда потоковая передача была включена. Поэтому я настоятельно рекомендую не обновлять вручную Textviews в Listview в отношении пользовательского интерфейса и производительности потока UI