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 ViewReaktion sehr langsam. Gibt es noch andere Lösungen?
Antworten:
Eine Möglichkeit besteht darin, das ListViewdirekt 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 Viewmit 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, targetist ein Element des Adapters.
Dieser Code ruft die ListViewaktuell angezeigten Ansichten ab, durchsucht sie und durchsucht das gesuchte targetElement mit den angezeigten Ansichtselementen. Wenn sich Ihr Ziel unter diesen befindet, rufen Sie die umschließende Ansicht auf und führen Sie den Adapter getViewfür diese Ansicht aus, um die Anzeige zu aktualisieren.
Da eine Randnotiz invalidatenicht wie erwartet funktioniert und die Ansicht nicht wie bei getView aktualisiert wird, notifyDataSetChangedwird die gesamte Liste neu erstellt und schließlich getviewalle angezeigten Elemente aufgerufen, was invalidateViewssich 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 getViewFall ist. In diesem Fall kann der folgende Code ersetzt werden list.getAdapter().getView(i, view, list);(Beispiel zum Ändern eines TextViewTextes):
((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_indexbedeutet 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 TextViewein Text "neu" angezeigt, und diese Ansicht wird festgelegt, GONEwenn 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 positionund 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.