Comment puis-je mettre à jour une seule ligne dans un ListView?

J'ai un ListView qui affiche les nouvelles. Ils contiennent une image, un titre et un texte. L'image est chargée dans un thread distinct (avec une file d'attente et tous) et lorsque l'image est téléchargée, j'appelle notifyDataSetChanged() sur l'adaptateur de liste pour mettre à jour l'image. Cela fonctionne, mais getView() est appelé trop souvent, puisque notifyDataSetChanged() appelle getView() pour tous les éléments visibles. Je souhaite mettre à jour le seul élément de la liste. Comment ferais-je cela?

Les problèmes que j'ai dans ma démarche actuelle sont les suivants:

  • Le fichier Android.bat est manquant dans le dossier sdk \ tools, mais je n'ai pas de gestionnaire AVD et SDK dans les outils afin de pouvoir les remplacer
  • Comment puis-je gérer plusieurs clients connectés à un serveur à l'aide de sockets?
  • Existe-t-il un moyen standard d'ajouter des divisions entre les éléments de la barre d'action dans Android 3.0?
  • Chargement de l'animation de la roue tournante
  • Erreur d'implémentation Admob
  • Différence entre la scène et l'écran de libgdx
    1. Le défilement est lent
    2. J'ai une animation fanée sur l'image qui se produit chaque fois qu'une seule nouvelle image dans la liste est chargée.

  • Comment ajouter un contact à un groupe Android
  • Android Studio Error: (3, 0) Plugin avec id 'com.android.application' introuvable
  • Ouverture d'une URL à l'aide du navigateur PlayBook natif à partir d'une application basée sur Android
  • Comment obtenir des abonnements Google Play périmés?
  • Remplacez le bouton d'alimentation tout comme le bouton Accueil
  • Comment réinitialiser un numéro de séquence d'auto-incréments dans sqlite
  • 10 Solutions collect form web for “Comment puis-je mettre à jour une seule ligne dans un ListView?”

    J'ai trouvé la réponse, grâce à vos informations Michelle. Vous pouvez en effet obtenir la bonne vue en utilisant View#getChildAt(int index) . La prise est qu'il commence à compter à partir du premier élément visible. En fait, vous ne pouvez obtenir que les éléments visibles. Vous résolvez ceci avec ListView#getFirstVisiblePosition() .

    Exemple:

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

    Cette question a été posée à Google I / O 2010, vous pouvez l'observer ici:

    Le monde de ListView, temps 52:30

    Fondamentalement, ce que Romain Guy explique, c'est appeler getChildAt(int) sur ListView pour obtenir la vue et (je pense) appeler getFirstVisiblePosition() pour trouver la corrélation entre position et index.

    Romain pointe également sur le projet appelé Etagères comme exemple, je pense qu'il pourrait signifier la méthode ShelvesActivity.updateBookCovers() , mais je ne peux pas trouver l'appel de getFirstVisiblePosition() .

    NOUVELLES ACTUALITÉS:

    The RecyclerView le réparera dans un proche avenir. Comme indiqué sur http://www.grokkingandroid.com/first-glance-androids-recyclerview/ , vous pourrez appeler des méthodes pour spécifier précisément le changement, par exemple:

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

    En outre, tout le monde voudra utiliser les nouvelles vues basées sur RecyclerView car elles seront récompensées par des animations très bénéfiques! Le futur semble génial! 🙂

    Voici comment je l'ai fait:

    Vos éléments (lignes) doivent avoir des identifiants uniques afin que vous puissiez les mettre à jour plus tard. Réglez la balise de chaque vue lorsque la liste obtient la vue à partir de l'adaptateur. (Vous pouvez également utiliser une clé si la variable par défaut est utilisée ailleurs

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

    Pour la mise à jour, vérifiez tous les éléments de la liste, si une vue avec un identifiant donné est visible, nous effectuons la mise à jour.

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

    C'est en fait plus facile que d'autres méthodes et mieux quand vous rencontrez des identifiants et non des positions! De plus, vous devez appeler la mise à jour pour les éléments qui sont visibles.

    Les réponses sont claires et correctes, je vais ajouter une idée pour le cas CursorAdapter ici.

    Si vous CursorAdapter (ou ResourceCursorAdapter ou SimpleCursorAdapter ), vous pouvez implémenter ViewBinder ou remplacer les bindView() et newView() , celles-ci ne reçoivent pas l'index actuel des éléments de liste dans les arguments. Par conséquent, lorsque certaines données arrivent et que vous souhaitez mettre à jour les éléments pertinents de la liste visible, comment connaissez-vous leurs indices?

    Ma solution a été de:

    • Conservez une liste de toutes les vues des éléments de liste créés, ajoutez des éléments à cette liste depuis newView()
    • Lorsque les données arrivent, les itérer et voir celles qui nécessitent une mise à jour – mieux que d'en faire notifyDatasetChanged() et rafraîchir toutes

    En raison du recyclage de la vue, le nombre de références de vues que je devrai stocker et d'itérer sera à peu près égal au nombre d'éléments de liste visible à l'écran.

    Obtenez la classe de modèle d'abord globale comme cet objet de classe de modèle

     SampleModel golbalmodel=new SchedulerModel(); 

    Et l'initialiser à l'échelle mondiale

    Obtenir la ligne actuelle de la vue par le modèle en initialisant le modèle global

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

    Définissez la valeur modifiée dans la méthode de l'objet du modèle global à définir et ajoutez la notificationDataSetChanged, elle fonctionne pour moi

     golbalmodel.setStartandenddate(changedate); notifyDataSetChanged(); 

    Voici une question connexe à ce sujet avec de bonnes réponses.

    J'ai utilisé le code qui a fourni Erik, fonctionne très bien, mais j'ai un adaptateur personnalisé complexe pour mon listview et j'ai été confronté à la mise en œuvre deux fois du code qui met à jour l'interface utilisateur. J'ai essayé d'obtenir la nouvelle vue à partir de ma méthode getView d'adaptateurs (l'arraylist qui détient les données listview a déjà été mis à jour / modifié):

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

    Cela fonctionne bien, mais je ne sais pas si c'est une bonne pratique. Je n'ai donc pas besoin de mettre en œuvre le code qui met à jour deux fois l'élément de la liste.

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

    Pour RecycleView, utilisez ce code

    Ma solution: si elle est correcte *, mettez à jour les données et les éléments visibles sans re-dessiner la liste entière. Sinon, notifiezDataSetChanged.

    Correct – oldData size == nouvelle taille de données, et les anciennes ID de données et leur ordre == nouvelles ID de données et ordre

    Comment:

     /** * 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; } 

    et bien sur:

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

    Ignorez le dernier paramètre pour bindView (je l'utilise pour déterminer si j'ai besoin de recycler les bitmaps pour ImageDrawable).

    Comme mentionné ci-dessus, le nombre total de vues "visibles" est approximativement le montant qui correspond à l'écran (ignorant les modifications d'orientation, etc.), donc pas de mémoire en mémoire.

    Exactement, j'ai utilisé cela

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

    J'ai inventé une autre solution, comme la méthode RecyclyerView void notifyItemChanged(int position) , crée une classe CustomBaseAdapter comme suit :

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

    N'oubliez pas de créer une classe CustomDataSetObservable aussi pour la variable mDataSetObservable dans CustomAdapterClass , comme ceci:

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

    Sur la classe CustomBaseAdapter, il existe une méthode notifyItemChanged(int position) , et vous pouvez appeler cette méthode lorsque vous souhaitez mettre à jour une ligne où vous voulez (à partir d'un clic de bouton ou n'importe où vous souhaitez appeler cette méthode). Et voila !, votre ligne unique se mettra à jour instantanément …

    coAndroid est un fan Android de Google, tout sur les téléphones Android, Android Wear, Android Dev et Android Games Apps.