Android ListView Einzelne Zeile aktualisieren

77

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?

Thizzer
quelle

Antworten:

30

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.

Daniel
quelle
3
Vielen Dank. Dies würde funktionieren, wenn ich das aktuelle Layout des Kindes bearbeite . Aber was ist, wenn ich das Layout komplett ändern möchte (indem ich eine andere XML aufblase)? Wenn ich newView(...)den Adapter aufrufe, gibt es mir eine andere Ansicht, die jedoch nicht in die eingefügt wird listView.
Hadi
1
@hadi Sie sollten einen Blick auf diese Antwort werfen
Jose_GD
90

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.

GriffonRL
quelle
17
Sie sollten wahrscheinlich nur Ihre andere Antwort auf stackoverflow.com/a/9987616/1026132 verlinken, anstatt sie hier zu kopieren und einzufügen . Dies würde dazu beitragen, die Menge an doppeltem Inhalt in SO zu reduzieren.
Nacho Coloma
2
Wie kann man dies mit einem anderen Ansichtstyp implementieren?
Zyoo
1
Der Link zu "Google I / O-Sitzung" als Antwort ist tot.
Pang
scheint großartig! Ich werde es heute versuchen
Rakesh Patil
Vielleicht müssen Sie manchmal "list.getAdapter (). getView (i - list.getHeaderviewCount, view, list)" verwenden. anstelle von "list.getAdapter (). getView (i, view, list);"
QuinnChen
28

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);
}
John Starr Dewar
quelle
2
Funktioniert wie ein Zauber, +1
Rutger
Ich werde das aktualisierte Wert sende eine weitere Seite Detail , wenn der Benutzer tut Änderungen auf dieser Seite Details , wie die Liste aktualisieren bitte zurück
Harsha
6

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 );
            }
        }
    }
}  
SkolVikingsGuy
quelle
in diesem Beispiel sysvar_indexbedeutet die Position der Zeile, die Sie aktualisieren möchten
Jemand irgendwo
2

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.

vedant
quelle
0

Für mich hat folgende Zeile funktioniert:

Arraylist.set (Position, neue ModelClass (Wert));

Adapter.notifyDataSetChanged ();

Girish
quelle
-2

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

                }
AnoNym
quelle
Die Frage war, wie eine einzelne Zeile in der ListView zu einem bestimmten Zeitpunkt aktualisiert werden kann. onItemClick wird nur aufgerufen, wenn die Benutzer auf ein Element klicken und daher nicht ausreichen
Thizzer