Après avoir obtenu les données d'une seule ligne d'un ListView
, je veux mettre à jour cette seule ligne.
Actuellement j'utilise notifyDataSetChanged();
mais cela fait réagir View
très lentement. Y a-t-il d'autres solutions?
Une option consiste à manipuler directement le ListView
. Vérifiez d'abord si l'index de la ligne mise à jour est compris entre getFirstVisiblePosition()
et getLastVisiblePosition()
, ces deux vous donnent les première et dernière positions de l'adaptateur qui sont visibles à l'écran. Ensuite, vous pouvez obtenir la ligne View
avec getChildAt(int index)
et la modifier.
Comme Romain Guy expliqué il y a quelque temps pendant la session Google I/O , le moyen le plus efficace de ne mettre à jour qu'une seule vue dans une vue de liste est quelque chose comme ceci (celui-ci mettre à jour toutes les données de la vue):
ListView list = getListView();
int start = list.getFirstVisiblePosition();
for(int i=start, j=list.getLastVisiblePosition();i<=j;i++)
if(target==list.getItemAtPosition(i)){
View view = list.getChildAt(i-start);
list.getAdapter().getView(i, view, list);
break;
}
En supposant que target
est un élément de l'adaptateur.
Ce code récupère le ListView
, puis parcourez les vues actuellement affichées, comparez l'élément target
que vous recherchez avec chaque élément de vue affiché, et si votre cible est parmi celles-ci, obtenez la vue englobante et exécutez l'adaptateur getView
sur cette vue pour actualiser l'affichage.
Comme note secondaire invalidate
ne fonctionne pas comme certaines personnes s'y attendent et ne rafraîchira pas la vue comme getView, notifyDataSetChanged
reconstruira toute la liste et finira par appeler getview
pour tous les éléments affichés et invalidateViews
affecteront également un groupe.
Une dernière chose, on peut également obtenir des performances supplémentaires s'il n'a besoin que de changer un enfant d'une vue de ligne et non la ligne entière comme le fait getView
. Dans ce cas, le code suivant peut remplacer list.getAdapter().getView(i, view, list);
(exemple pour modifier un texte TextView
):
((TextView)view.findViewById(R.id.myid)).setText("some new text");
En code, nous avons confiance.
Cette méthode plus simple fonctionne bien pour moi, et il vous suffit de connaître l'indice de position pour obtenir la vue:
// mListView is an instance variable
private void updateItemAtPosition(int position) {
int visiblePosition = mListView.getFirstVisiblePosition();
View view = mListView.getChildAt(position - visiblePosition);
mListView.getAdapter().getView(position, view, mListView);
}
Le code suivant a fonctionné pour moi. Notez que lorsque vous appelez GetChild (), vous devez compenser par le premier élément de la liste car il est relatif à cela.
int iFirst = getFirstVisiblePosition();
int iLast = getLastVisiblePosition();
if ( indexToChange >= numberOfRowsInSection() ) {
Log.i( "MyApp", "Invalid index. Row Count = " + numberOfRowsInSection() );
}
else {
if ( ( index >= iFirst ) && ( index <= iLast ) ) {
// get the view at the position being updated - need to adjust index based on first in the list
View vw = getChildAt( sysvar_index - iFirst );
if ( null != vw ) {
// get the text view for the view
TextView tv = (TextView) vw.findViewById(com.Android.myapp.R.id.scrollingListRowTextView );
if ( tv != null ) {
// update the text, invalidation seems to be automatic
tv.setText( "Item = " + myAppGetItem( index ) + ". Index = " + index + ". First = " + iFirst + ". Last = " + iLast );
}
}
}
}
Il y a une autre chose beaucoup plus efficace que vous pouvez faire, si cela correspond à votre cas d'utilisation.
Si vous changez d'état et pouvez en quelque sorte appeler le bon (en connaissant la position) mListView.getAdapter().getView()
ce sera le plus efficace de tous.
Je peux démontrer un moyen très simple de le faire, en créant une classe interne anonyme dans ma classe ListAdapter.getView()
. Dans cet exemple, j'ai un TextView
montrant un texte "nouveau" et cette vue est définie sur GONE
lorsque l'élément de liste est cliqué:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// assign the view we are converting to a local variable
View view = convertView;
Object quotation = getItem(position);
// first check to see if the view is null. if so, we have to inflate it.
if (view == null)
view = mInflater.inflate(R.layout.list_item_quotation, parent, false);
final TextView newTextView = (TextView) view.findViewById(R.id.newTextView);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mCallbacks != null)
mCallbacks.onItemSelected(quotation.id);
if (!quotation.isRead()) {
servicesSingleton.setQuotationStatusReadRequest(quotation.id);
quotation.setStatusRead();
newTextView.setVisibility(View.GONE);
}
}
});
if(quotation.isRead())
newTextView.setVisibility(View.GONE);
else
newTextView.setVisibility(View.VISIBLE);
return view;
}
Le framework utilise automatiquement le position
correct et vous devez vous soucier de le récupérer à nouveau avant d'appeler getView
.