Tengo un ListView que muestra los elementos de noticias. Contienen una imagen, un título y un texto. La imagen se carga en un hilo separado (con una cola y todo) y cuando se descargue la imagen, ahora llamo a notifyDataSetChanged() en la lista adaptador para actualizar la imagen. Esto funciona, pero getView() es recibir llamadas con demasiada frecuencia, ya que notifyDataSetChanged() llamadas getView() para todos los elementos visibles. Quiero actualizar solo el elemento en la lista. ¿Cómo puedo hacer esto?

Problemas que tengo con mi actual enfoque son:

  1. Desplazamiento es lento
  2. Tengo una animación de fade-in en la imagen de lo que ocurre cada vez que una nueva imagen en la lista se carga.
InformationsquelleAutor Erik | 2010-09-16

10 Comentarios

  1. 194

    He encontrado la respuesta, gracias a su información Michelle.
    De hecho, puede obtener el derecho de la vista uso de View#getChildAt(int index). El problema es que se comienza a contar desde el primer elemento visible. De hecho, usted puede conseguir solamente los elementos visibles. Puedes resolver este con ListView#getFirstVisiblePosition().

    Ejemplo:

    private void updateView(int index){
        View v = yourListView.getChildAt(index - 
            yourListView.getFirstVisiblePosition());
    
        if(v == null)
           return;
    
        TextView someText = (TextView) v.findViewById(R.id.sometextview);
        someText.setText("Hi! I updated you manually!");
    }
    • nota personal: mImgView.setImageURI() actualizar el elemento de la lista, pero sólo una vez; así que estoy mejor con mLstVwAdapter.notifyDataSetInvalidated() en su lugar.
    • Esta solución funciona. Pero esto sólo puede hacerse con yourListView.getChildAt(índice); y cambiar la vista. Ambas soluciones de trabajo!!
    • u me puede ayudar en este stackoverflow.com/questions/18312539/…
    • Gracias … por Hacer mi trabajo
    • ¿qué pasaría si me acaba de llamar getView() en el adaptador sólo si la posición es entre getFirstVisiblePosition() y getLastVisiblePosition() ? funciona de la misma? creo que va a actualizar la vista, como de costumbre, derecho?
    • En mi caso, algunas veces vista es nulo, debido a que cada elemento que se actualice de forma asíncrona, lo mejor para comprobar si la vista != null
    • funcionando a la perfección sin ningún tipo de problemas. gracias por la respuesta.
    • Parece útil, voy a tratar de implementar una sola fila actualizar (cambiar ImageButton imagen) para replicar Instagram Como botón. Parte difícil es que necesito para asegurarse de que el servicio de llamadas de AsyncTask la respuesta coincide con el estado del Botón. Te voy a dar un tiro con una Emisión receptor.
    • Las grandes obras. Que me ayude a terminar la tarea de la implementación de la función Como en un instagram-como app. Yo estaba usando un adaptador personalizado, una emisión en el botón «me gusta» dentro de un elemento de la lista, el lanzamiento de un asynctask para decirle al backend acerca de la como con una emisión receptor en el padre fragmento, y, finalmente, Erik de respuesta a la actualización de la lista.
    • Impresionante el Trabajo…Muchas Gracias:)))
    • Brillante . Guardar mi día a día .:)
    • Muchas gracias. Entendí la posición visible de la estrategia.
    • Que es el «índice» de aquí? Es el índice de la fila?
    • No trabajo para mi ListView Personalizado Adaptador. Necesito actualizar fila en el Botón haga clic en
    • Muchas gracias. Incluso después de 8 años, este funciona a la perfección.

  2. 70

    Esta pregunta se ha hecho en el Google I/O 2010, puedes verlo aquí:

    El mundo del ListView, el tiempo de 52:30

    Básicamente lo que Romain Guy explica es llamar getChildAt(int) en el ListView para obtener la vista y (creo) la llamada getFirstVisiblePosition() para averiguar la correlación entre la posición y el índice.

    Romain también señala que el proyecto denominado Estantes como un ejemplo, creo que podría significar que el método ShelvesActivity.updateBookCovers(), pero no puedo encontrar la llamada de getFirstVisiblePosition().

    IMPRESIONANTE ACTUALIZACIONES DE VENIR:

    La RecyclerView va a arreglar esto en el futuro cercano. Como se señaló en http://www.grokkingandroid.com/first-glance-androids-recyclerview/, usted será capaz de llamar a los métodos para especificar exactamente el cambio, tales como:

    void notifyItemInserted(int position)
    void notifyItemRemoved(int position)
    void notifyItemChanged(int position)

    Además, todo el mundo se quiere el uso de los nuevos puntos de vista basados en RecyclerView porque ellos serán recompensados con bien-mirando animaciones! El futuro se ve impresionante! 🙂

    • He publicado la solución. Gracias por tu info!
    • Buena cosa! 🙂 Tal vez usted puede agregar una verificación null aquí, demasiado – v puede ser null si la vista no está disponible en el momento. Y, por supuesto, estos datos se ha ido si el usuario desplaza el ListView, por lo que también se debería actualizar los datos en el adaptador (tal vez sin llamar notifyDataSetChanged()). En general, creo que sería una buena idea para mantener todo esto dentro de la lógica de la adaptador, es decir, pasando el ListView referencia a él.
    • Esto funcionó para mí, excepto cuando la vista a la izquierda de la pantalla, cuando se vuelve a introducir el cambio se restablece. Supongo que porque en el getview es todavía la recepción de la información original. ¿Cómo puedo evitar esto?
    • Una respuesta con bucle for stackoverflow.com/a/9987616/241986
  3. 6

    Esta es la forma en que lo hice:

    Sus elementos (filas), debe tener un id exclusivo para poder actualizar más tarde. La etiqueta de cada vista cuando la lista es la vista desde el adaptador. (También puede utilizar la tecla de etiqueta si la etiqueta predeterminada se utiliza en algún otro lugar)

    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {
        View view = super.getView(position, convertView, parent);
        view.setTag(getItemId(position));
        return view;
    }

    Para la actualización de verificación de cada elemento de la lista, si miras con id es no es visible, para que nos lleve a cabo la actualización.

    private void update(long id)
    {
    
        int c = list.getChildCount();
        for (int i = 0; i < c; i++)
        {
            View view = list.getChildAt(i);
            if ((Long)view.getTag() == id)
            {
                //update view
            }
        }
    }

    Es en realidad más fácil que otros métodos y mejor cuando se trata con ids de posiciones! También debe llamar a la actualización de los elementos que se haga visible.

  4. 3

    obtener el modelo de primera clase mundial como este modelo de objeto de la clase

    SampleModel golbalmodel=new SchedulerModel();

    y llegará a global

    obtener la fila actual de la vista por el modelo de inicialización de los que el modelo global

    SampleModel data = (SchedulerModel) sampleList.get(position);
                    golbalmodel=data;

    establecer el valor modificado para el modelo global método de objeto de ajustar y añadir el notifyDataSetChanged sus obras para mí

    golbalmodel.setStartandenddate(changedate);
    
    notifyDataSetChanged();

    Aquí está una pregunta relacionada con las buenas respuestas.

    • Este es el enfoque correcto, ya que usted está recibiendo el objeto que desea actualizar, actualizar y luego notificar al adaptador, y que va a cambiar la información de la fila con los nuevos datos.
  5. 2

    Las respuestas son claras y correctas, voy a añadir una idea para CursorAdapter caso aquí.

    Si vas a crear subclases de CursorAdapter (o ResourceCursorAdapter, o SimpleCursorAdapter), luego de llegar a implementar ViewBinder o anular bindView() y newView() métodos, estos no reciben elemento de lista actual índice de argumentos. Por lo tanto, cuando algunos llegan los datos y desea actualizar relevantes visibles los elementos de la lista, ¿cómo sabemos que sus índices?

    Mi solución fue:

    • mantener una lista de todo lo creado elemento de la lista de vistas, agregar elementos a esta lista de newView()
    • cuando llegan los datos, recorrer y ver que una de las necesidades de actualización, mejor que haciendo notifyDatasetChanged() y refrescante todos ellos

    Debido a vista de reciclaje el número de la vista de las referencias que voy a necesitar para almacenar y recorrer será más o menos igual al número de elementos de la lista visible en la pantalla.

  6. 2
    int wantedPosition = 25; //Whatever position you're looking for
    int firstPosition = linearLayoutManager.findFirstVisibleItemPosition(); //This is the same as child #0
    int wantedChild = wantedPosition - firstPosition;
    
    if (wantedChild < 0 || wantedChild >= linearLayoutManager.getChildCount()) {
        Log.w(TAG, "Unable to get view for desired position, because it's not being displayed on screen.");
        return;
    }
    
    View wantedView = linearLayoutManager.getChildAt(wantedChild);
    mlayoutOver =(LinearLayout)wantedView.findViewById(R.id.layout_over);
    mlayoutPopup = (LinearLayout)wantedView.findViewById(R.id.layout_popup);
    
    mlayoutOver.setVisibility(View.INVISIBLE);
    mlayoutPopup.setVisibility(View.VISIBLE);

    Para RecycleView por favor, utilice este código

    • he implementado esta en recycleView, funciona. Pero cuando usted se desplaza a la lista se vuelve a cambiar otra vez. alguna ayuda?
    • Usar get y settag
  7. 1

    He usado el código que proporciona Erik, funciona perfecto, pero tengo un complejo adaptador personalizado para mi listview y me encontré con el doble de la implementación del código que actualiza la interfaz de usuario. He tratado de conseguir que la nueva vista de mi adaptadores método getView(arraylist que contiene el listview de datos ya ha sido actualizado/modificado):

    View cell = lvOptim.getChildAt(index - lvOptim.getFirstVisiblePosition());
    if(cell!=null){
        cell = adapter.getView(index, cell, lvOptim); //public View getView(final int position, View convertView, ViewGroup parent)
        cell.startAnimation(animationLeftIn());
    }

    Está funcionando bien, pero no sé si esto es una buena práctica.
    Así que no es necesario implementar el código que actualiza el elemento de la lista dos veces.

  8. 1

    exactamente he utilizado este

    private void updateSetTopState(int index) {
            View v = listview.getChildAt(index -
                    listview.getFirstVisiblePosition()+listview.getHeaderViewsCount());
    
            if(v == null)
                return;
    
            TextView aa = (TextView) v.findViewById(R.id.aa);
            aa.setVisibility(View.VISIBLE);
        }
  9. 1

    Hice otra solución, como RecyclyerView método void notifyItemChanged(int position), crear CustomBaseAdapter clase como esta:

    public abstract class CustomBaseAdapter implements ListAdapter, SpinnerAdapter {
        private final CustomDataSetObservable mDataSetObservable = new CustomDataSetObservable();
    
        public boolean hasStableIds() {
            return false;
        }
    
        public void registerDataSetObserver(DataSetObserver observer) {
            mDataSetObservable.registerObserver(observer);
        }
    
        public void unregisterDataSetObserver(DataSetObserver observer) {
            mDataSetObservable.unregisterObserver(observer);
        }
    
        public void notifyDataSetChanged() {
            mDataSetObservable.notifyChanged();
        }
    
        public void notifyItemChanged(int position) {
            mDataSetObservable.notifyItemChanged(position);
        }
    
        public void notifyDataSetInvalidated() {
            mDataSetObservable.notifyInvalidated();
        }
    
        public boolean areAllItemsEnabled() {
            return true;
        }
    
        public boolean isEnabled(int position) {
            return true;
        }
    
        public View getDropDownView(int position, View convertView, ViewGroup parent) {
            return getView(position, convertView, parent);
        }
    
        public int getItemViewType(int position) {
            return 0;
        }
    
        public int getViewTypeCount() {
            return 1;
        }
    
        public boolean isEmpty() {
            return getCount() == 0;
        } {
    
        }
    }

    No te olvides de crear un CustomDataSetObservable clase demasiado para mDataSetObservable variable en CustomAdapterClass, como este:

    public class CustomDataSetObservable extends Observable<DataSetObserver> {
    
        public void notifyChanged() {
            synchronized(mObservers) {
                //since onChanged() is implemented by the app, it could do anything, including
                //removing itself from {@link mObservers} - and that could cause problems if
                //an iterator is used on the ArrayList {@link mObservers}.
                //to avoid such problems, just march thru the list in the reverse order.
                for (int i = mObservers.size() - 1; i >= 0; i--) {
                    mObservers.get(i).onChanged();
                }
            }
        }
    
        public void notifyInvalidated() {
            synchronized (mObservers) {
                for (int i = mObservers.size() - 1; i >= 0; i--) {
                    mObservers.get(i).onInvalidated();
                }
            }
        }
    
        public void notifyItemChanged(int position) {
            synchronized(mObservers) {
                //since onChanged() is implemented by the app, it could do anything, including
                //removing itself from {@link mObservers} - and that could cause problems if
                //an iterator is used on the ArrayList {@link mObservers}.
                //to avoid such problems, just march thru the list in the reverse order.
                mObservers.get(position).onChanged();
            }
        }
    }

    en clase CustomBaseAdapter hay un método notifyItemChanged(int position), y usted puede llamar a ese método cuando desee actualizar una fila donde quieras (desde el botón o haga clic en cualquier lugar que desee llamar a ese método). Y voila!, su única fila que se va a actualizar al instante..

  10. 0

    Mi solución:
    Si es correcto*, actualizar los datos y ver los elementos sin necesidad de volver a dibujar la totalidad de la lista. Otra cosa notifyDataSetChanged.

    Correcta – oldData tamaño == nuevo tamaño de los datos, y los datos antiguos IDs y su orden == nuevos datos Identificadores y el orden

    Cómo:

    /**
    * A View can only be used (visible) once. This class creates a map from int (position) to view, where the mapping
    * is one-to-one and on.
    * 
    */
    private static class UniqueValueSparseArray extends SparseArray<View> {
    private final HashMap<View,Integer> m_valueToKey = new HashMap<View,Integer>();
    @Override
    public void put(int key, View value) {
    final Integer previousKey = m_valueToKey.put(value,key);
    if(null != previousKey) {
    remove(previousKey);//re-mapping
    }
    super.put(key, value);
    }
    }
    @Override
    public void setData(final List<? extends DBObject> data) {
    //TODO Implement 'smarter' logic, for replacing just part of the data?
    if (data == m_data) return;
    List<? extends DBObject> oldData = m_data;
    m_data = null == data ? Collections.EMPTY_LIST : data;
    if (!updateExistingViews(oldData, data)) notifyDataSetChanged();
    else if (DEBUG) Log.d(TAG, "Updated without notifyDataSetChanged");
    }
    /**
    * See if we can update the data within existing layout, without re-drawing the list.
    * @param oldData
    * @param newData
    * @return
    */
    private boolean updateExistingViews(List<? extends DBObject> oldData, List<? extends DBObject> newData) {
    /**
    * Iterate over new data, compare to old. If IDs out of sync, stop and return false. Else - update visible
    * items.
    */
    final int oldDataSize = oldData.size();
    if (oldDataSize != newData.size()) return false;
    DBObject newObj;
    int nVisibleViews = m_visibleViews.size();
    if(nVisibleViews == 0) return false;
    for (int position = 0; nVisibleViews > 0 && position < oldDataSize; position++) {
    newObj = newData.get(position);
    if (oldData.get(position).getId() != newObj.getId()) return false;
    //iterate over visible objects and see if this ID is there.
    final View view = m_visibleViews.get(position);
    if (null != view) {
    //this position has a visible view, let's update it!
    bindView(position, view, false);
    nVisibleViews--;
    }
    }
    return true;
    }

    y de curso:

    @Override
    public View getView(final int position, final View convertView, final ViewGroup parent) {
    final View result = createViewFromResource(position, convertView, parent);
    m_visibleViews.put(position, result);
    return result;
    }

    Ignorar la última param a bindView (yo lo uso para determinar si es o no necesito a la papelera de mapas de bits para ImageDrawable).

    Como se mencionó anteriormente, el número total de ‘visible’ puntos de vista es más o menos la cantidad que cabe en la pantalla (sin tener en cuenta los cambios de orientación, etc), así que no hay problema de memoria sabio.

Dejar respuesta

Please enter your comment!
Please enter your name here