Підтримуючи / зберігаючи / відновлюючи позицію прокрутки, повертаючись до ListView


397

У мене є довго, ListViewщо користувач може прокручувати навколо себе, перш ніж повернутися на попередній екран. Коли користувач відкриє це ListViewзнову, я хочу, щоб список прокручувався до тієї ж точки, що і раніше. Будь-які ідеї, як цього досягти?


22
Я думаю, що рішення, про які згадував Євген Мімрін / Джорджіо Барчісі, є кращими за прийняту відповідь
Міра Веллер

Відповіді:


631

Спробуйте це:

// save index and top position
int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : (v.getTop() - mList.getPaddingTop());

// ...

// restore index and position
mList.setSelectionFromTop(index, top);

Пояснення:

ListView.getFirstVisiblePosition()повертає верхній видимий елемент списку. Але цей елемент може бути частково прокручений із поля перегляду, і якщо ви хочете відновити точне положення прокрутки у списку, вам потрібно отримати це зміщення. Таким чином, ListView.getChildAt(0)повертає Viewелемент для верхнього списку, а потім View.getTop() - mList.getPaddingTop()повертає відносне зміщення від верхньої частини ListView. Потім, щоб відновити ListViewпозицію прокрутки, ми зателефонуємо ListView.setSelectionFromTop()за допомогою індексу потрібного елемента та зміщенням, щоб розташувати його верхній край від верхньої частини ListView.


2
Не дуже розуміючи, як це працює. Я використовую тільки listView.getFirstVisiblePosition () і розумію це, але не впевнений, що тут відбувається. Будь-який шанс короткого пояснення? :)
HXCaine

56
Отже ListView.getFirstVisiblePosition () повертає верхній видимий елемент списку. Але цей елемент може бути частково прокручений із поля перегляду, і якщо ви хочете відновити точне положення прокрутки у списку, вам потрібно отримати це зміщення. Отже ListView.getChildAt (0) повертає Погляд для елемента верхнього списку, а потім View.getTop () повертає відносне зміщення у верхній частині ListView. Потім, щоб відновити позицію прокрутки ListView, ми викликаємо ListView.setSelectionFromTop () з індексом потрібного елемента та зміщенням, щоб розташувати його верхній край від верхньої частини ListView. Все чисто?
Ян

15
Це може бути зроблено ще простіше використовувати один рядок , щоб зберегти: int index = mList.getFirstVisiblePosition();і тільки одна лінія для відновлення: mList.setSelectionFromTop(index, 0);. Хоча чудова відповідь (+1)! Я шукав елегантне рішення цієї проблеми.
Філ

17
@Запоможте, що ваше спрощене рішення не працює для відновлення точної позиції прокрутки.
Ixx

2
як сказав @nbarraille, код повинен читати більше як "v.getTop () - mList.getPaddingTop ()." Інакше ти проведеш годину, як я намагався з'ясувати, чому вона завжди відновлює лише волосся ...
jdowdell

544
Parcelable state;

@Override
public void onPause() {    
    // Save ListView state @ onPause
    Log.d(TAG, "saving listview state");
    state = listView.onSaveInstanceState();
    super.onPause();
}
...

@Override
public void onViewCreated(final View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    // Set new items
    listView.setAdapter(adapter);
    ...
    // Restore previous state (including selected item index and scroll position)
    if(state != null) {
        Log.d(TAG, "trying to restore listview state");
        listView.onRestoreInstanceState(state);
    }
}

106
Я думаю, що це найкраща відповідь, оскільки цей метод відновлює точне положення прокрутки, а не лише перший видимий елемент.
Міра Веллер

9
Чудова відповідь, але не буде працювати при динамічному завантаженні вмісту, і ви хочете зберегти стан у методі onPause ().
Gio

13
чудове рішення. Всього одна проблема. Якщо елементи великі, і ви прокручуєте, але перший елемент все ще видно, список переходить наверх. якщо ви перейдете до другого пункту чи деінде, все працює
passsy

4
@passsy Ви коли-небудь знайшли рішення, щоб список повернувся до вершини?
Papajohn000

2
Як сказав aaronvargas, це не вдалося, коли ListView.getFirstVisiblePosition () дорівнює 0.
VinceStyling

54

Я прийняв рішення, запропоноване @ (Кірк Волл), і воно працює для мене. Я також бачив у вихідному коді Android для програми "Контакти", що вони використовують подібну техніку. Я хотів би додати ще детальну інформацію: Нагорі в моєму класі, отриманому ListActivity:

private static final String LIST_STATE = "listState";
private Parcelable mListState = null;

Тоді деякий метод перекриває:

@Override
protected void onRestoreInstanceState(Bundle state) {
    super.onRestoreInstanceState(state);
    mListState = state.getParcelable(LIST_STATE);
}

@Override
protected void onResume() {
    super.onResume();
    loadData();
    if (mListState != null)
        getListView().onRestoreInstanceState(mListState);
    mListState = null;
}

@Override
protected void onSaveInstanceState(Bundle state) {
    super.onSaveInstanceState(state);
    mListState = getListView().onSaveInstanceState();
    state.putParcelable(LIST_STATE, mListState);
}

Звичайно, "loadData" - це моя функція для отримання даних із БД та внесення їх у список.

На моєму пристрої Froyo це працює як при зміні орієнтації телефону, так і під час редагування елемента та повернення до списку.


1
Це рішення спрацювало на мене. Інші - ні. Як поводиться збереження / відновлення стану екземпляра ListView щодо видалення та вставки елементів у ListView?
Брайан

2
FYI, якщо ви використовуєте це у фрагменті (я використовую фрагмент списку) та переходите до інших фрагментів, це вийде з ладу, оскільки подання вмісту не створюється при виклику mListState = getListView().onSaveInstanceState().головним чином при обертанні пристрою.
Mgamerz

2
onRestoreInstanceStateніколи не називається :(
dVaffection

1
@GiorgioBarchiesi Хто є @ (Кірк Вул), рішення якого працює для вас? Користувач, очевидно, змінив своє ім'я.
Піовезан

1
Так, це офіційна релігія. Але в моєму випадку дані завантажуються локально, мають дуже обмежену довжину і завантажуються за долі секунди, тому я пішов еретично :-)
Giorgio Barchiesi

26

Дуже простий спосіб:

/** Save the position **/
int currentPosition = listView.getFirstVisiblePosition();

//Here u should save the currentPosition anywhere

/** Restore the previus saved position **/
listView.setSelection(savedPosition);

Метод setSelection скине список на доданий елемент. Якщо в режимі сенсорного режиму немає, елемент буде фактично обраний, якщо в режимі дотику елемент буде розміщений лише на екрані.

Складніший підхід:

listView.setOnScrollListener(this);

//Implements the interface:
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
    mCurrentX = view.getScrollX();
    mCurrentY = view.getScrollY();
}

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {

}

//Save anywere the x and the y

/** Restore: **/
listView.scrollTo(savedX, savedY);

2
у вашій відповіді сталася помилка. Метод називається setSelection
Януш

Ідея працює добре, але є псевдопроблема. Перше видиме положення може бути показане лише небагато (можливо, лише невелика частина рядка) та setSelection () встановить цей рядок повністю видимим, коли відновлення буде зроблено.
rantravee

Будь ласка, погляньте на мій другий варіант. Я щойно додав це до своєї першої відповіді
Франческо Лауріта

27
view.getScrollX () і view.getScrollY () завжди повертають 0!
rantravee

3
Погляньте на моє (прийняте) рішення вище, використовуючи View.getChildAt () та View.getTop (). Ви не можете використовувати View.getScrollY () для ListView, оскільки ListView підтримує свою прокрутку внутрішньо.
Ян

17

Я знайшов щось цікаве в цьому.

Я спробував setSelection і scrolltoXY, але це не спрацювало зовсім, список залишився в тому ж самому положенні, після деяких проб і помилок я отримав наступний код, який працює

final ListView list = (ListView) findViewById(R.id.list);
list.post(new Runnable() {            
    @Override
    public void run() {
        list.setSelection(0);
    }
});

Якщо замість публікації Runnable ви спробуєте runOnUiThread, це не працює (принаймні, на деяких пристроях)

Це дуже дивне рішення для чогось, що повинно бути прямо вперед.


1
У мене виникло те саме питання І setSelectionFromTop()не працює.
ofavre

+1. listnew.post (), не працює на деяких пристроях, а на деяких інших пристроях у певних випадках не працює, але runOnUIThread працює чудово.
Омар Рехман

@Omar, чи можете ви навести кілька прикладів пристроїв та ОС, на яких це не працює? Я спробував HTC G2 (2.3.4) та Galaxy Nexus (4.1.2), і він працював на обох. Жоден з інших відповідей у ​​цій темі не працював ні на одному з моїх пристроїв.
Dan J

2
listview.post чудово працює на моєму Galaxy Note з 4.0.4, але не працює на моєму Nexus One з 2.3.6. Однак onOnUIThread (дія) чудово працює на обох пристроях.
Омар Рехман

11

ОБЕРЕЖНО !! Існує помилка в AbsListView, яка не дозволяє програмі onSaveState () правильно працювати, якщо ListView.getFirstVisiblePosition () дорівнює 0.

Отже, якщо у вас є великі зображення, які займають більшу частину екрана, і ви переходите до другого зображення, але трохи першого відображається, позиція прокрутки не буде збережена ...

з AbsListView.java:1650 (коментарі мої)

// this will be false when the firstPosition IS 0
if (haveChildren && mFirstPosition > 0) {
    ...
} else {
    ss.viewTop = 0;
    ss.firstId = INVALID_POSITION;
    ss.position = 0;
}

Але в цій ситуації "верх" у наведеному нижче коді буде від'ємним числом, що спричиняє інші проблеми, які заважають правильному відновленню стану. Тож, коли «верх» негативний, знайдіть наступну дитину

// save index and top position
int index = getFirstVisiblePosition();
View v = getChildAt(0);
int top = (v == null) ? 0 : v.getTop();

if (top < 0 && getChildAt(1) != null) {
    index++;
    v = getChildAt(1);
    top = v.getTop();
}
// parcel the index and top

// when restoring, unparcel index and top
listView.setSelectionFromTop(index, top);

Це працює як шарм, але я не повністю його розумію. Якщо перший предмет все ще видно, як має сенс отримати наступну дитину? Чи не повинен встановитиSelectionFromTop з новим індексом причину відображення списку починаючи з другої дитини? Як я все ще бачу перший?
tafi

@tafi, перший елемент може бути "частково" видимим. Тож верхній елемент цього пункту знаходився б над екраном, і, таким чином, було б від’ємне число. (Це спричиняє інші проблеми, яких я не пригадую ...) Тому ми використовуємо "верхню частину" другого пункту для розміщення, яка завжди повинна бути доступною (?), Інакше не буде прокрутки в першу чергу !
aaronvargas

6
private Parcelable state;
@Override
public void onPause() {
    state = mAlbumListView.onSaveInstanceState();
    super.onPause();
}

@Override
public void onResume() {
    super.onResume();

    if (getAdapter() != null) {
        mAlbumListView.setAdapter(getAdapter());
        if (state != null){
            mAlbumListView.requestFocus();
            mAlbumListView.onRestoreInstanceState(state);
        }
    }
}

Цього достатньо


Привіт, чи допоможе мені ваш код вище зі списком RecyclerView, де екземпляр не зберігається між діями? Я розмістив тут таке питання: stackoverflow.com/questions/35413495/… ?
AJW

6

Для тих, хто шукає рішення цієї проблеми, корінь проблеми може бути там, де ви встановлюєте адаптер перегляду списків списку. Після встановлення адаптера на перегляд списку він скидає положення прокрутки. Просто щось врахувати. Я перемістив налаштування адаптера в свій onCreateView після того, як ми захопили посилання на listview, і це вирішило для мене проблему. =)


1

Я публікую це, тому що я дивуюсь, що ніхто про це не згадував.

Після натискання кнопки "Назад" користувач повернеться до перегляду списку в тому ж стані, з якого він вийшов.

Цей код замінить кнопку "вгору", щоб поводитись так само, як і кнопку назад, тому у випадку перегляду Listlist -> Деталі -> Назад до перегляду списків (та інших опцій) це найпростіший код для підтримки прокрутки та вмісту у списку перегляду.

 public boolean onOptionsItemSelected(MenuItem item) {
     switch (item.getItemId()) {
         case android.R.id.home:
             onBackPressed();
             return(true);
     }
     return(super.onOptionsItemSelected(item)); }

Застереження: Якщо ви можете перейти до іншої діяльності, починаючи з детальної діяльності, кнопка вгору поверне вас до цієї діяльності, тому вам доведеться маніпулювати історією зворотних кнопок, щоб це працювало.



1

НАЙКРАЩЕ РІШЕННЯ:

// save index and top position
int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : (v.getTop() - mList.getPaddingTop());

// ...

// restore index and position
mList.post(new Runnable() {
    @Override
    public void run() {
      mList.setSelectionFromTop(index, top);
   }
});

ВАМ ПОВИННІ ЗАБАВИТИ В ПОШТУ ТА В ТИПУ!


1

Ви можете підтримувати стан прокрутки після перезавантаження, якщо зберегти стан перед перезавантаженням та відновити його після. У моєму випадку я зробив запит на асинхронну мережу та перезавантажив список у зворотний дзвінок після його завершення. Тут я відновлюю стан. Зразок коду - Котлін.

val state = myList.layoutManager.onSaveInstanceState()

getNewThings() { newThings: List<Thing> ->

    myList.adapter.things = newThings
    myList.layoutManager.onRestoreInstanceState(state)
}

0

Якщо ви використовуєте фрагменти, розміщені на заході, ви можете зробити щось подібне:

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());
          }
     }
}

0

Якщо ви зберігаєте / відновлюєте позицію прокрутки, ListViewви по суті дублюєте функціонал, вже реалізований в android Framework. ListViewВідновлює точне положення прокрутки тільки добре сама по собі , за винятком один нюанс: у @aaronvargas згадується є помилка в AbsListViewтому , що не дозволить відновити позицію прокрутки штрафу для першого елемента списку. Тим не менш, найкращий спосіб відновити положення прокрутки - це не відновити її. Android Framework зробить це краще для вас. Просто переконайтеся, що ви виконали такі умови:

  • переконайтеся, що ви не викликали setSaveEnabled(false)метод і не встановили android:saveEnabled="false"атрибут для списку у файлі макета xml
  • для методу ExpandableListViewпереопределення, long getCombinedChildId(long groupId, long childId)щоб він повертав позитивне довге число (реалізація за замовчуванням у класі BaseExpandableListAdapterповертає негативне число). Ось приклади:

.

@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;
}
  • якщо ListViewабо ExpandableListViewвикористовується в фрагменті, не відтворюйте фрагмент для активного відпочинку (наприклад, після обертання екрана). Отримайте фрагмент findFragmentByTag(String tag)методом.
  • переконайтеся, що це ListViewє, android:idі це унікально.

Щоб уникнути вищезазначеного застереження з першим елементом списку, ви можете скласти адаптер так, як він повертає спеціальні манекени з переглядом висоти нульових пікселів для ListViewпозиції 0. Ось простий приклад проекту показує ListViewта ExpandableListViewвідновлення їх точних позицій прокрутки, тоді як позиції прокрутки явно не зберігаються / відновлено. Точне положення прокрутки відновлюється ідеально навіть для складних сценаріїв з тимчасовим перемиканням на якусь іншу програму, подвійним обертанням екрана та поверненням до тестової програми. Зверніть увагу, якщо ви явно закриваєте програму (натискаючи кнопку Назад), позиція прокрутки не буде збережена (як і всі інші представлення не збереже їх стан). https://github.com/voromto/RestoreScrollPosition/releases


0

Для діяльності, похідної від ListActivity, що реалізує LoaderManager.LoaderCallbacks за допомогою SimpleCursorAdapter, вона не працювала, щоб відновити позицію в onReset (), оскільки діяльність майже завжди перезапускалася і адаптер перезавантажувався, коли перегляд деталей закривався. Хитрість полягала в тому, щоб відновити позицію в onLoadFinished ():

в 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());

в onLoadFinished ():

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

в onBackPress ():

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

0

Жодне із запропонованих тут рішень для мене не здавалося. У моєму випадку, у мене є ListViewв Fragmentякий я замінити в FragmentTransaction, так що новий Fragmentекземпляр створюється кожен раз , коли фрагмент показаний, який означає , що ListViewдержава не може бути збережений в якості члена з Fragment.

Натомість я закінчив зберігати стан у своєму спеціальному Applicationкласі. Код нижче повинен дати вам уявлення про те, як це працює:

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 ... */

}

Основна ідея полягає в тому, що ви зберігаєте стан поза екземпляром фрагмента. Якщо вам не подобається ідея статичного поля у вашому класі додатків, я думаю, ви могли це зробити, застосувавши інтерфейс фрагментів та зберігаючи стан у своїй діяльності.

Іншим рішенням буде збереження його SharedPreferences, але воно стає дещо складнішим, і вам потрібно переконатися, що ви очистите його при запуску програми, якщо ви не хочете, щоб стан зберігався під час запуску програм.

 

Крім того, щоб уникнути "положення прокрутки, не збереженого, коли видно перший елемент", ви можете відобразити перший елемент з манекеном з 0pxвисотою. Цього можна досягти шляхом переосмислення getView()адаптера, наприклад:

@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);
}

0

Моя відповідь стосується Firebase, а позиція 0 - це спосіб вирішення

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) {
        }
    });

і convertView

позиція 0 a додайте порожній предмет, який ви не використовуєте

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;
}
}

Я бачив цю роботу тимчасово, не заважаючи користувачеві, сподіваюся, вона працює для вас


0

використовувати цей код нижче:

int index,top;

@Override
protected void onPause() {
    super.onPause();
    index = mList.getFirstVisiblePosition();

    View v = challengeList.getChildAt(0);
    top = (v == null) ? 0 : (v.getTop() - mList.getPaddingTop());
}

і коли ви оновлюєте свої дані, використовуйте цей код нижче:

adapter.notifyDataSetChanged();
mList.setSelectionFromTop(index, top);

0

Я використовую FirebaseListAdapter і не зміг отримати жодне рішення. Я закінчила це робити. Я здогадуюсь, що є більш елегантні способи, але це повне і діюче рішення.

Перед тим, як створити:

private int reset;
private int top;
private int index;

Всередині FirebaseListAdapter:

@Override
public void onDataChanged() {
     super.onDataChanged();

     // Only do this on first change, when starting
     // activity or coming back to it.
     if(reset == 0) {
          mListView.setSelectionFromTop(index, top);
          reset++;
     }

 }

onStart:

@Override
protected void onStart() {
    super.onStart();
    if(adapter != null) {
        adapter.startListening();
        index = 0;
        top = 0;
        // Get position from SharedPrefs
        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
        top = sharedPref.getInt("TOP_POSITION", 0);
        index = sharedPref.getInt("INDEX_POSITION", 0);
        // Set reset to 0 to allow change to last position
        reset = 0;
    }
}

onStop:

@Override
protected void onStop() {
    super.onStop();
    if(adapter != null) {
        adapter.stopListening();
        // Set position
        index = mListView.getFirstVisiblePosition();
        View v = mListView.getChildAt(0);
        top = (v == null) ? 0 : (v.getTop() - mListView.getPaddingTop());
        // Save position to SharedPrefs
        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
        sharedPref.edit().putInt("TOP_POSITION" + "", top).apply();
        sharedPref.edit().putInt("INDEX_POSITION" + "", index).apply();
    }
}

Оскільки я також повинен був вирішити це для FirebaseRecyclerAdapter, я розміщую рішення і для цього:

Перед тим, як створити:

private int reset;
private int top;
private int index;

Всередині FirebaseRecyclerAdapter:

@Override
public void onDataChanged() {
    // Only do this on first change, when starting
    // activity or coming back to it.
    if(reset == 0) {
        linearLayoutManager.scrollToPositionWithOffset(index, top);
        reset++;
    }
}

onStart:

@Override
protected void onStart() {
    super.onStart();
    if(adapter != null) {
        adapter.startListening();
        index = 0;
        top = 0;
        // Get position from SharedPrefs
        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
        top = sharedPref.getInt("TOP_POSITION", 0);
        index = sharedPref.getInt("INDEX_POSITION", 0);
        // Set reset to 0 to allow change to last position
        reset = 0;
    }
}

onStop:

@Override
protected void onStop() {
    super.onStop();
    if(adapter != null) {
        adapter.stopListening();
        // Set position
        index = linearLayoutManager.findFirstVisibleItemPosition();
        View v = linearLayoutManager.getChildAt(0);
        top = (v == null) ? 0 : (v.getTop() - linearLayoutManager.getPaddingTop());
        // Save position to SharedPrefs
        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
        sharedPref.edit().putInt("TOP_POSITION" + "", top).apply();
        sharedPref.edit().putInt("INDEX_POSITION" + "", index).apply();
    }
}

0

Щоб уточнити чудову відповідь Райана Ньюсома та налаштувати її на фрагменти та на звичайний випадок, що ми хочемо перейти від "головного" фрагмента ListView до фрагмента "деталі", а потім повернутися до "головного"

    private View root;
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
           if(root == null){
             root = inflater.inflate(R.layout.myfragmentid,container,false);
             InitializeView(); 
           } 
           return root; 
        }

    public void InitializeView()
    {
        ListView listView = (ListView)root.findViewById(R.id.listviewid);
        BaseAdapter adapter = CreateAdapter();//Create your adapter here
        listView.setAdpater(adapter);
        //other initialization code
    }

"Магія" тут полягає в тому, що коли ми повертаємося назад з фрагмента деталей до фрагмента ListView, подання не відтворюється, ми не встановлюємо адаптер ListView, тому все залишається таким, як ми його залишили!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.