Join FlipAndroid.COM Telegram Group: https://t.me/joinchat/F_aqThGkhwcLzmI49vKAiw


¿Cómo puedo actualizar una sola fila en un ListView?

Tengo un ListView que muestra las 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 descarga la imagen, ahora llamo a notifyDataSetChanged() en el adaptador de lista para actualizar la imagen. Esto funciona, pero getView() se llama demasiado, ya que notifyDataSetChanged() llama a getView() para todos los elementos visibles. Quiero actualizar sólo el elemento individual de la lista. ¿Cómo haría esto?

Los problemas que tengo con mi enfoque actual son:

  • ¿Por qué querría alguna vez `setRetainInstance (false)`? - O - La forma correcta de manejar la rotación del dispositivo
  • Descarga rápidamente mapas de bits de la memoria
  • ¿Cómo acceder a widgets en una DialogPreference personalizada con un diseño inflado?
  • Transmitir video en vivo de un teléfono a otro utilizando socket fd
  • Abrir perfil de usuario de aplicaciones de Twitter desde la aplicación de Android
  • ¿Qué pasó con la eliminación de elementos de realm.io? RealmException "No se admite el objeto de eliminación".?
    1. El desplazamiento es lento
    2. Tengo una animación de fade-in en la imagen que sucede cada vez que una sola imagen nueva en la lista se carga.

  • La ejecución de Gradle falló para la tarea ': app: dexDebug' con el nuevo proyecto de Android Studio
  • Cordova WebView TIMEOUT ERROR con android
  • Audiencia de eventos personalizada en la aplicación para Android
  • Soporte completo de Android para paquetes OSGi
  • ¿Cuál es la transición predeterminada entre las actividades de Android 4.0 (API 14+)
  • Autocompletetextview setonitemselectedlistener no funciona
  • 10 Solutions collect form web for “¿Cómo puedo actualizar una sola fila en un ListView?”

    He encontrado la respuesta, gracias a su información Michelle. De hecho, puede obtener la vista correcta utilizando View#getChildAt(int index) . La captura es que comienza a contar desde el primer elemento visible. De hecho, sólo se pueden obtener los elementos visibles. Soluciona esto 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!"); } 

    Esta pregunta se ha hecho en la I / O de Google 2010, puedes verla aquí:

    El mundo de ListView, tiempo 52:30

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

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

    ACTUALIZACIONES IMPRESIONANTES QUE VENGAN:

    El RecyclerView arreglará esto en un futuro próximo. Como se señala en http://www.grokkingandroid.com/first-glance-androids-recyclerview/ , usted podrá llamar a métodos para especificar exactamente el cambio, como:

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

    Además, todos querrán usar las nuevas vistas basadas en RecyclerView porque serán recompensadas con animaciones de aspecto agradable. ¡El futuro parece increíble! Todos los derechos reservados

    Así es como lo hice:

    Sus elementos (filas) deben tener identificadores únicos para que pueda actualizarlos más tarde. Establezca la etiqueta de cada vista cuando la lista obtiene la vista desde el adaptador. (También puede utilizar la etiqueta clave si la etiqueta predeterminada se utiliza en 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 comprueba cada elemento de la lista, si una vista con el identificador dado está allí es visible así que realizamos 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 realmente más fácil que otros métodos y mejor cuando se trata de ids no posiciones! También debe llamar a la actualización de los elementos que se hacen visibles.

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

    Si está subclasificando CursorAdapter (o ResourceCursorAdapter , o SimpleCursorAdapter ), entonces puede llegar a implementar ViewBinder o sustituir bindView() y newView() , estos no reciben el índice de elemento de lista actual en argumentos. Por lo tanto, cuando algunos datos llegan y desea actualizar elementos relevantes de la lista visible, ¿cómo conoce sus índices?

    Mi solución fue:

    • Mantener una lista de todas las vistas de elemento de lista creadas, agregar elementos a esta lista desde newView()
    • Cuando los datos llegan, iterarlos y ver cuál necesita una actualización – mejor que haciendo notifyDatasetChanged() y actualizar todos ellos

    Debido a ver el reciclaje el número de referencias de vista que necesitaré almacenar e iterar será aproximadamente igual al número de elementos de lista visibles en la pantalla.

    Obtener la clase de modelo primero como global como este objeto de clase de modelo

     SampleModel golbalmodel=new SchedulerModel(); 

    Y lo inicializan a global

    Obtener la fila actual de la vista por el modelo inicializando el modelo global

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

    Establecer el valor cambiado a método de objeto de modelo global que se establecerá y agregar el notifyDataSetChanged sus trabajos para mí

     golbalmodel.setStartandenddate(changedate); notifyDataSetChanged(); 

    Aquí hay una pregunta relacionada con respuestas buenas.

    He utilizado el código que proporcionó Erik, funciona muy bien, pero tengo un adaptador personalizado complejo para mi listview y me enfrenté con dos veces la aplicación del código que actualiza la interfaz de usuario. He intentado obtener la nueva vista de mi método getView de adaptadores (el arraylist que contiene los datos de listview 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 necesito implementar el código que actualiza el elemento de lista dos veces.

     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

    Mi solución: Si es correcta *, actualice los datos y los elementos visibles sin volver a dibujar la lista completa. Else notifyDataSetChanged.

    Correcto – oldData tamaño == nuevo tamaño de datos, y antiguos ID de datos y su orden == nuevos ID de datos y 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 por supuesto:

     @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; } 

    Ignora el último param para bindView (lo uso para determinar si necesito o no reciclar mapas de bits para ImageDrawable).

    Como se mencionó anteriormente, el número total de vistas "visibles" es aproximadamente la cantidad que se ajusta en la pantalla (ignorando los cambios de orientación, etc), por lo que no biggie memoria-sabio.

    Exactamente usé esto

     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); } 

    He inventado otra solución, como el método RecyclyerView void notifyItemChanged(int position) , crear CustomBaseAdapter clase así:

     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 olvide crear una clase CustomDataSetObservable también para la variable mDataSetObservable en CustomAdapterClass , como esto:

     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 la clase CustomBaseAdapter hay un método notifyItemChanged(int position) , y puedes llamar a ese método cuando quieras actualizar una fila donde quieras (desde el botón haga clic o en cualquier lugar que quieras llamar a ese método). Y voila !, su fila única se actualizará al instante ..

    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.