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?
Antworten
Zu viele Anzeigen?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.
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);
}
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;
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 dieandroid:saveEnabled="false"
Attribut für die Liste in der Xml-Layout-Datei - für
ExpandableListView
Überschreiben Sielong getCombinedChildId(long groupId, long childId)
Methode, so dass sie eine positive lange Zahl zurückgibt (Standardimplementierung in der KlasseBaseExpandableListAdapter
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
oExpandableListView
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 mitfindFragmentByTag(String tag)
método. - stellen Sie sicher, dass die
ListView
hat eineandroid: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
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());
}
}
}
- See previous answers
- Weitere Antworten anzeigen