Nachdem ich die Daten für eine einzelne Zeile von a erhalten habe ListView
, möchte ich diese einzelne Zeile aktualisieren.
Momentan benutze ich notifyDataSetChanged();
aber das macht die View
Reaktion sehr langsam. Gibt es noch andere Lösungen?
Antworten:
Eine Möglichkeit besteht darin, das ListView
direkt zu manipulieren . Überprüfen Sie zunächst, ob der Index der aktualisierten Zeile zwischen getFirstVisiblePosition()
und getLastVisiblePosition()
liegt. Diese beiden geben Ihnen die erste und letzte Position im Adapter an, die auf dem Bildschirm angezeigt werden. Dann können Sie die Zeile View
mit abrufen getChildAt(int index)
und ändern.
Wie Romain Guy vor einiger Zeit während der Google I / O-Sitzung erklärte, ist die effizienteste Methode, nur eine Ansicht in einer Listenansicht zu aktualisieren, etwa die folgende (diese aktualisiert die gesamten Ansichtsdaten):
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;
}
Angenommen, target
ist ein Element des Adapters.
Dieser Code ruft die ListView
aktuell angezeigten Ansichten ab, durchsucht sie und durchsucht das gesuchte target
Element mit den angezeigten Ansichtselementen. Wenn sich Ihr Ziel unter diesen befindet, rufen Sie die umschließende Ansicht auf und führen Sie den Adapter getView
für diese Ansicht aus, um die Anzeige zu aktualisieren.
Da eine Randnotiz invalidate
nicht wie erwartet funktioniert und die Ansicht nicht wie bei getView aktualisiert wird, notifyDataSetChanged
wird die gesamte Liste neu erstellt und schließlich getview
alle angezeigten Elemente aufgerufen, was invalidateViews
sich auch auf eine Reihe auswirkt.
Eine letzte Sache ist, dass man auch zusätzliche Leistung erhalten kann, wenn man nur ein Kind einer Zeilenansicht ändern muss und nicht die gesamte Zeile wie dies der getView
Fall ist. In diesem Fall kann der folgende Code ersetzt werden list.getAdapter().getView(i, view, list);
(Beispiel zum Ändern eines TextView
Textes):
((TextView)view.findViewById(R.id.myid)).setText("some new text");
Auf Code vertrauen wir.
Diese einfachere Methode funktioniert gut für mich, und Sie müssen nur den Positionsindex kennen, um die Ansicht zu erhalten:
// 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);
}
Der folgende Code hat bei mir funktioniert. Beachten Sie, dass Sie beim Aufrufen von GetChild () um das erste Element in der Liste versetzen müssen, da es relativ dazu ist.
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 );
}
}
}
}
sysvar_index
bedeutet die Position der Zeile, die Sie aktualisieren möchten
Es gibt noch eine viel effizientere Möglichkeit, wenn sie zu Ihrem Anwendungsfall passt.
Wenn Sie den Status ändern und irgendwie die richtige (durch Kenntnis der Position) aufrufen können, ist mListView.getAdapter().getView()
dies die effizienteste von allen.
Ich kann einen wirklich einfachen Weg zeigen, indem ich eine anonyme innere Klasse in meiner ListAdapter.getView()
Klasse erstelle . In diesem Beispiel wird TextView
ein Text "neu" angezeigt, und diese Ansicht wird festgelegt, GONE
wenn auf das Listenelement geklickt wird:
@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;
}
Das Framework verwendet automatisch das richtige position
und Sie müssen sich vor dem Aufruf darum kümmern, es erneut abzurufen getView
.
Hat jemand die 'Ansichtsansicht' für die Überschreibungsmethode in Betracht gezogen?
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//Update the selected item
((TextView)view.findViewById(R.id.cardText2)).setText("done!");
}
newView(...)
den Adapter aufrufe, gibt es mir eine andere Ansicht, die jedoch nicht in die eingefügt wirdlistView
.