414 Stimmen

Beibehalten/Speichern/Wiederherstellen der Bildlaufposition bei der Rückkehr zu einer ListView

Ich habe eine lange ListView dass der Benutzer einen Bildlauf durchführen kann, bevor er zum vorherigen Bildschirm zurückkehrt. Wenn der Benutzer diese Seite öffnet ListView Ich möchte, dass die Liste wieder an die gleiche Stelle wie zuvor gescrollt wird. Irgendwelche Ideen, wie man das erreichen kann?

0voto

Trk Punkte 135

Meine Antwort ist für Firebase und Position 0 ist ein Workaround

Parcelable state;

DatabaseReference everybody = db.getReference("Everybody Room List");
    everybody.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            state = listView.onSaveInstanceState(); // Save
            progressBar.setVisibility(View.GONE);
            arrayList.clear();
            for (DataSnapshot messageSnapshot : dataSnapshot.getChildren()) {
                Messages messagesSpacecraft = messageSnapshot.getValue(Messages.class);
                arrayList.add(messagesSpacecraft);
            }
            listView.setAdapter(convertView);
            listView.onRestoreInstanceState(state); // Restore
        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {
        }
    });

und convertView

Position 0 a fügen Sie ein leeres Element ein, das Sie nicht verwenden

public class Chat_ConvertView_List_Room extends BaseAdapter {

private ArrayList<Messages> spacecrafts;
private Context context;

@SuppressLint("CommitPrefEdits")
Chat_ConvertView_List_Room(Context context, ArrayList<Messages> spacecrafts) {
    this.context = context;
    this.spacecrafts = spacecrafts;
}

@Override
public int getCount() {
    return spacecrafts.size();
}

@Override
public Object getItem(int position) {
    return spacecrafts.get(position);
}

@Override
public long getItemId(int position) {
    return position;
}

@SuppressLint({"SetTextI18n", "SimpleDateFormat"})
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        convertView = LayoutInflater.from(context).inflate(R.layout.message_model_list_room, parent, false);
    }

    final Messages s = (Messages) this.getItem(position);

    if (position == 0) {
        convertView.getLayoutParams().height = 1; // 0 does not work
    } else {
        convertView.getLayoutParams().height = RelativeLayout.LayoutParams.WRAP_CONTENT;
    }

    return convertView;
}
}

Ich habe gesehen, dass dies vorübergehend funktioniert, ohne den Benutzer zu stören. Ich hoffe, es funktioniert bei Ihnen.

0voto

Magnus Punkte 14381

Keine der hier angebotenen Lösungen schien bei mir zu funktionieren. In meinem Fall habe ich eine ListView in einem Fragment die ich durch eine FragmentTransaction also eine neue Fragment Instanz wird jedes Mal erstellt, wenn das Fragment angezeigt wird, was bedeutet, dass die ListView Zustand kann nicht als Mitglied der Fragment .

Stattdessen habe ich den Status in meiner benutzerdefinierten Application Klasse. Der folgende Code soll Ihnen eine Vorstellung davon vermitteln, wie dies funktioniert:

public class MyApplication extends Application {
    public static HashMap<String, Parcelable> parcelableCache = new HashMap<>();

    /* ... code omitted for brevity ... */
}

public class MyFragment extends Fragment{
    private ListView mListView = null;
    private MyAdapter mAdapter = null;

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        mAdapter = new MyAdapter(getActivity(), null, 0);
        mListView = ((ListView) view.findViewById(R.id.myListView));

        Parcelable listViewState = MyApplication.parcelableCache.get("my_listview_state");
        if( listViewState != null )
            mListView.onRestoreInstanceState(listViewState);
    }

    @Override
    public void onPause() {
        MyApplication.parcelableCache.put("my_listview_state", mListView.onSaveInstanceState());
        super.onPause();
    }

    /* ... code omitted for brevity ... */

}

Die Grundidee ist, dass Sie den Zustand außerhalb der Fragmentinstanz speichern. Wenn Ihnen die Idee eines statischen Feldes in Ihrer Anwendungsklasse nicht gefällt, könnten Sie eine Fragmentschnittstelle implementieren und den Zustand in Ihrer Aktivität speichern.

Eine andere Lösung wäre, die Daten in SharedPreferences aber es wird etwas komplizierter, und Sie müssen sicherstellen, dass Sie es beim Start der Anwendung löschen, es sei denn, Sie wollen, dass der Status über mehrere Starts der Anwendung hinweg erhalten bleibt.

Um das Problem "Scroll-Position wird nicht gespeichert, wenn das erste Element sichtbar ist" zu vermeiden, können Sie auch ein Dummy-Element mit 0px Höhe. Dies kann erreicht werden durch Überschreiben von getView() in Ihrem Adapter, etwa so:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if( position == 0 ) {
        View zeroHeightView = new View(parent.getContext());
        zeroHeightView.setLayoutParams(new ViewGroup.LayoutParams(0, 0));
        return zeroHeightView;
    }
    else
        return super.getView(position, convertView, parent);
}

0voto

Perotin Punkte 1

Bei einer von ListActivity abgeleiteten Aktivität, die LoaderManager.LoaderCallbacks implementiert und einen SimpleCursorAdapter verwendet, funktionierte es nicht, die Position in onReset() wiederherzustellen, da die Aktivität fast immer neu gestartet und der Adapter neu geladen wurde, wenn die Detailansicht geschlossen wurde. Der Trick war, die Position in onLoadFinished() wiederherzustellen:

in onListItemClick():

// save the selected item position when an item was clicked
// to open the details
index = getListView().getFirstVisiblePosition();
View v = getListView().getChildAt(0);
top = (v == null) ? 0 : (v.getTop() - getListView().getPaddingTop());

in onLoadFinished():

// restore the selected item which was saved on item click
// when details are closed and list is shown again
getListView().setSelectionFromTop(index, top);

in onBackPressed():

// Show the top item at next start of the app
index = 0;
top = 0;

0voto

Sergey Punkte 940

Wenn Sie die Bildlaufposition speichern/wiederherstellen von ListView Sie duplizieren im Wesentlichen die bereits im Android-Framework implementierte Funktionalität. Die ListView stellt die korrekte Bildlaufposition von selbst wieder her, mit einer Ausnahme: Wie @aaronvargas erwähnte, gibt es einen Fehler in AbsListView die nicht zulässt, dass die Position für das erste Listenelement wiederhergestellt wird. Der beste Weg, die Bildlaufposition wiederherzustellen, ist jedoch, sie nicht wiederherzustellen. Das Android-Framework wird das besser für Sie erledigen. Stellen Sie einfach sicher, dass Sie die folgenden Bedingungen erfüllt haben:

  • Vergewissern Sie sich, dass Sie nicht aufgerufen haben setSaveEnabled(false) Methode und nicht die android:saveEnabled="false" Attribut für die Liste in der Xml-Layout-Datei
  • für ExpandableListView Überschreiben Sie long getCombinedChildId(long groupId, long childId) Methode, so dass sie eine positive lange Zahl zurückgibt (Standardimplementierung in der Klasse BaseExpandableListAdapter gibt eine negative Zahl zurück). Hier sind Beispiele:

.

@Override
public long getChildId(int groupPosition, int childPosition) {
    return 0L | groupPosition << 12 | childPosition;
}

@Override
public long getCombinedChildId(long groupId, long childId) {
    return groupId << 32 | childId << 1 | 1;
}

@Override
public long getGroupId(int groupPosition) {
    return groupPosition;
}

@Override
public long getCombinedGroupId(long groupId) {
    return (groupId & 0x7FFFFFFF) << 32;
}
  • wenn ListView o ExpandableListView in einem Fragment verwendet wird, wird das Fragment bei der Wiederherstellung der Aktivität (z. B. nach einer Bildschirmdrehung) nicht neu erstellt. Holen Sie sich das Fragment mit findFragmentByTag(String tag) método.
  • stellen Sie sicher, dass die ListView hat eine android:id und sie ist einzigartig.

Um die oben erwähnte Einschränkung mit dem ersten Listenelement zu vermeiden, können Sie Ihren Adapter so gestalten, dass er eine spezielle Dummy-Ansicht mit einer Höhe von null Pixeln für die ListView an Position 0. Hier ist das einfache Beispielprojekt zeigt ListView y ExpandableListView ihre Feinbildposition wiederherstellen, während ihre Bildlaufpositionen nicht explizit gespeichert/wiederhergestellt werden. Die Feinbildposition wird auch bei komplexen Szenarien mit vorübergehendem Wechsel zu einer anderen Anwendung, doppelter Bildschirmdrehung und Rückwechsel zur Testanwendung perfekt wiederhergestellt. Bitte beachten Sie, dass beim expliziten Verlassen der Anwendung (durch Drücken der Zurück-Taste) die Scroll-Position nicht gespeichert wird (ebenso wie alle anderen Ansichten ihren Zustand nicht speichern). https://github.com/voromto/RestoreScrollPosition/releases

0voto

Pedro Andrade Punkte 4337

Wenn Sie Fragmente verwenden, die für eine Aktivität gehostet werden, können Sie etwa so vorgehen:

public abstract class BaseFragment extends Fragment {
     private boolean mSaveView = false;
     private SoftReference<View> mViewReference;

     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
          if (mSaveView) {
               if (mViewReference != null) {
                    final View savedView = mViewReference.get();
                    if (savedView != null) {
                         if (savedView.getParent() != null) {
                              ((ViewGroup) savedView.getParent()).removeView(savedView);
                              return savedView;
                         }
                    }
               }
          }

          final View view = inflater.inflate(getFragmentResource(), container, false);
          mViewReference = new SoftReference<View>(view);
          return view;
     }

     protected void setSaveView(boolean value) {
           mSaveView = value;
     }
}

public class MyFragment extends BaseFragment {
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
          setSaveView(true);
          final View view = super.onCreateView(inflater, container, savedInstanceState);
          ListView placesList = (ListView) view.findViewById(R.id.places_list);
          if (placesList.getAdapter() == null) {
               placesList.setAdapter(createAdapter());
          }
     }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X