2011-05-01 23 views
13

Tengo un hilo de fondo cargando datos que quiero mostrar en un ListView de Android. Los datos cambian muy a menudo (es decir, 1-2 veces por segundo). Algunas veces, el número de filas en el conjunto de datos también cambia (pero ciertamente no tan a menudo como cambian los datos en las celdas).¿Cómo actualizar Android ListView con datos dinámicos en tiempo real?

Hay dos maneras de actualizar los datos de las celdas, por lo que yo puedo decir:

  1. Tener el subproceso de fondo notificar al hilo de interfaz de usuario que los nuevos datos está listo, y el hilo de interfaz de usuario puede entonces llame a BaseAdapter.notifyDataSetChanged(). Sin embargo, he leído en más de un lugar que si ese método se llama con frecuencia, será lento, porque ListView tiene que reestructurar todas sus subvistas.

  2. Si el recuento de los conjuntos de datos no ha cambiado, posiblemente podría encontrar todas las celdas visibles de ListView que están asociadas con los datos modificados, y actualizar los valores manualmente sin llamar a notifyDataSetChanged(). Probablemente esto funcionaría, pero creo que es una pena que tenga que actualizar las vistas manualmente cuando se supone que el Adaptador de listas maneja las notificaciones y los mecanismos de actualización cuando se lo notifico. Este método tampoco funcionará si el recuento de datos cambia con el tiempo (es decir, no solo cambian los datos dentro de cada celda del ListView, sino que el número total de celdas en el ListView puede crecer o reducirse según el hilo de fondo que proporciona datos en tiempo real)

sin duda lo apreciarán los pensamientos de otras personas que han implementado este escenario, y cómo optimizar código de simplicidad y lo más importante, el rendimiento.

Respuesta

7

I experimentado con ListView, y que esencialmente tiene que actualizar manualmente las células ListView sin llamar notifyDataSetChanged() si tiene datos en tiempo real y desea que el ListView para actualizar con un mejor rendimiento.

notifyDataSetChanged() hace que el ListView reconstruya toda su jerarquía View es muy lento si lo está llamando con frecuencia (es decir, una vez por segundo).

Nota (2014). Esto NO APLICA si está utilizando el ListView moderno normal con un patrón ViewHolder. Simplemente llame a 'notifyDataSetChanged' y eso es todo. Es increíblemente eficiente ya que Android sabe que solo actualiza las celdas en la pantalla.

Implementé un esquema donde si mi tamaño de conjunto de datos cambió de una actualización a la siguiente, llamo al notifyDataSetChanged(). Si el tamaño del conjunto de datos se mantuvo constante de una actualización a la siguiente (de tal manera que el número de células en el ListView es el mismo que antes, cuando se necesita un redibujado de los datos), entonces iterar sobre la ListView.getFirstVisiblePosition(): getLastVisiblePosition(), y actualizar el celdas visibles solamente

+0

Un ListView no contiene una vista para cada objeto en su adaptador. Las vistas se vuelven a usar, por lo que las vistas visibles son las únicas que hay. Entonces su implementación no está optimizando nada. – pgsandstrom

+0

Usted debe mirar en el patrón de adaptadores ViewHolder –

2

Una vez implementé un filtro como el código anterior usando notifyDataSetChanged() y no tuve problemas con él.

También he modificado las vistas de una lista sobre la marcha de forma manual. Ambos han funcionado bien. En algún caso prefiero modificar los datos manualmente porque es más rápido y porque no afecta a toda la lista.

De todos modos, las vistas son creados sobre la marcha cuando tienen que apear en la pantalla y se eliminan al salir de la pantalla, por lo que si modifica los datos utilizados para crear los puntos de vista, si el usuario se desplaza el ListView y las vistas salga de la pantalla, en teoría, las vistas se crearán con los nuevos datos una vez que vuelvan a aparecer en la pantalla.

Le recomendaría que pruebe el siguiente código para comprender cómo funciona notifyDataSetChanged() y decida si le conviene.

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", "", "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> 

Espero que esto ayude.

+0

Olvidé algo. En este escenario i utiliza una imagen fija, pero en mi programa de descargo las fotos y me muestro una imagen de carga hasta que se haya descargado el Pictre, entonces modificar de forma manual mediante la localización de la vista. Funciona igual que espero que funcione (con una selección de amigos de followFriends de más de 700 amigos). No uso 'notifyDataSetChanged()' para actualizar las imágenes porque no es necesario y al hacerlo manualmente, el programa no se vuelve lento. –

1

Quizás pueda establecer un controlador en el hilo de la interfaz de usuario. Necesita crear una clase que implemente Runnable. Pase un ArrayList a esta clase. En el método run() crea el adaptador con ArrayList como parámetro, luego haz setAapter en ListView. Eso es. Ya terminaste Para iniciar su controlador: solo para esto desde su hilo de trabajo: handler.Post (new MyUpdateToUI()); Eso es todo. Espero que sea lo suficientemente eficiente para ti?

Cuestiones relacionadas