Duy trì / Lưu / Khôi phục vị trí cuộn khi quay lại ListView


397

Tôi có một thời gian dài ListView mà người dùng có thể cuộn xung quanh trước khi quay lại màn hình trước đó. Khi người dùng mở ListViewlại cái này , tôi muốn danh sách được cuộn đến cùng điểm với nó trước đó. Bất kỳ ý tưởng về làm thế nào để đạt được điều này?


22
Tôi nghĩ rằng các giải pháp được đề cập bởi Eugene Mymrin / Giorgio Barchiesi tốt hơn câu trả lời được chấp nhận
Mira Weller

Câu trả lời:


631

Thử cái này:

// 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);

Giải thích:

ListView.getFirstVisiblePosition()trả về mục danh sách hiển thị hàng đầu. Nhưng mục này có thể được cuộn một phần khỏi tầm nhìn và nếu bạn muốn khôi phục vị trí cuộn chính xác của danh sách, bạn cần lấy phần bù này. Vì vậy, ListView.getChildAt(0)trả về Viewmục danh sách hàng đầu, và sau đó View.getTop() - mList.getPaddingTop()trả về phần bù tương đối của nó từ đầu mục ListView. Sau đó, để khôi phục ListViewvị trí cuộn của chúng tôi, chúng tôi gọi ListView.setSelectionFromTop()với chỉ mục của mục chúng tôi muốn và một phần bù để định vị cạnh trên của nó từ đỉnh của ListView.


2
Không thực sự hiểu làm thế nào điều này hoạt động. Tôi chỉ sử dụng listView.getFirstVisibleP vị trí () và hiểu điều đó, nhưng không chắc chắn điều gì đang xảy ra ở đây. Bất kỳ cơ hội của một lời giải thích ngắn gọn? :)
HXCaine

56
Vì vậy, ListView.getFirstVisibleP vị trí () trả về mục danh sách hiển thị hàng đầu. Nhưng mục này có thể được cuộn một phần khỏi tầm nhìn và nếu bạn muốn khôi phục vị trí cuộn chính xác của danh sách, bạn cần lấy phần bù này. Vì vậy, ListView.getChildAt (0) trả về View cho mục danh sách hàng đầu và sau đó View.getTop () trả về phần bù tương đối của nó từ đầu ListView. Sau đó, để khôi phục vị trí cuộn của ListView, chúng tôi gọi ListView.setSelectionFromTop () với chỉ mục của mục chúng tôi muốn và một phần bù để định vị cạnh trên của nó từ đỉnh ListView. Tất cả rõ ràng?
ian

15
Điều này có thể được thực hiện thậm chí đơn giản hơn bằng cách sử dụng một dòng để lưu: int index = mList.getFirstVisiblePosition();và chỉ một dòng để khôi phục : mList.setSelectionFromTop(index, 0);. Câu trả lời tuyệt vời mặc dù (+1)! Tôi đã tìm kiếm một giải pháp tao nhã cho vấn đề này.
Phil

17
@Phil giải pháp đơn giản của bạn không hoạt động để khôi phục vị trí cuộn chính xác.
Ixx

2
như @nbarraille đã nói, mã nên đọc nhiều hơn như "v.getTop () - mList.getPaddingTop ()." Nếu không, bạn sẽ dành một giờ như tôi đã cố gắng tìm ra lý do tại sao nó luôn phục hồi chỉ là một sợi tóc ...
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
Tôi nghĩ rằng đây là câu trả lời tốt nhất vì phương pháp này khôi phục vị trí cuộn chính xác và không chỉ mục hiển thị đầu tiên.
Mira Weller

9
Câu trả lời tuyệt vời nhưng sẽ không hoạt động khi tải động nội dung và bạn muốn lưu trạng thái trong phương thức onPause ().
Gio

13
giải pháp tuyệt vời. Chỉ một vấn đề. Nếu các mục lớn và bạn cuộn, nhưng mục đầu tiên vẫn hiển thị, danh sách sẽ nhảy trở lại đầu trang. nếu bạn cuộn đến mục thứ hai hoặc bất cứ nơi nào khác thì mọi thứ đều hoạt động
passsy

4
@passsy Bạn đã bao giờ tìm thấy một giải pháp danh sách nhảy trở lại đầu trang?
Papajohn000

2
Như aaronvargas đã nói, điều này không thể hoạt động khi ListView.getFirstVisibleP vị trí () bằng 0.
VinceStyling

54

Tôi đã áp dụng giải pháp được đề xuất bởi @ (Kirk Woll) và nó hiệu quả với tôi. Tôi cũng đã thấy trong mã nguồn Android cho ứng dụng "Danh bạ", họ sử dụng một kỹ thuật tương tự. Tôi muốn thêm một số chi tiết: Trên đầu trang của lớp có nguồn gốc ListActivity của tôi:

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

Sau đó, một số phương thức ghi đè:

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

Tất nhiên "loadData" là chức năng của tôi để lấy dữ liệu từ DB và đưa nó vào danh sách.

Trên thiết bị Froyo của tôi, tính năng này hoạt động cả khi bạn thay đổi hướng điện thoại và khi bạn chỉnh sửa một mục và quay lại danh sách.


1
Giải pháp này đã làm việc cho tôi. Những người khác thì không. Làm thế nào để lưu / khôi phục trạng thái đối tượng ListView hoạt động liên quan đến việc xóa và chèn các mục trong ListView?
Bryan

2
FYI nếu bạn sử dụng phần này trong một đoạn (tôi đang sử dụng một đoạn danh sách) và điều hướng đến các đoạn khác, điều này sẽ sụp đổ vì chế độ xem nội dung không được tạo khi gọi mListState = getListView().onSaveInstanceState(). Chủ yếu khi xoay thiết bị.
Mgamerz

2
onRestoreInstanceState không bao giờ được gọi là :(
dVaffection

1
@GiorgioBarchiesi Ai là @ (Kirk Wool) ai là giải pháp phù hợp với bạn? Người dùng rõ ràng đã thay đổi tên của mình.
Piovezan 4/2/2015

1
Vâng, đó là tôn giáo chính thức. Nhưng trong trường hợp của tôi, dữ liệu được tải cục bộ, có độ dài rất hạn chế và tải trong tích tắc, vì vậy tôi đã đi sâu vào :-)
Giorgio Barchiesi

26

Một cách rất đơn giản:

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

//Here u should save the currentPosition anywhere

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

Phương thức setSelection sẽ đặt lại danh sách thành mục được cung cấp. Nếu không ở chế độ cảm ứng, vật phẩm sẽ thực sự được chọn nếu ở chế độ cảm ứng, vật phẩm sẽ chỉ được định vị trên màn hình.

Một cách tiếp cận phức tạp hơn:

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
có một lỗi trong câu trả lời của bạn. Phương thức này được gọi là setSelection
Janusz

Ý tưởng hoạt động tốt, nhưng có một vấn đề giả. Vị trí hiển thị đầu tiên có thể chỉ được hiển thị một chút, (có thể chỉ là một phần nhỏ của hàng) và setSelection () đặt hàng này hiển thị hoàn toàn khi khôi phục thực hiện.
câu thần chú

Xin hãy xem lựa chọn thứ hai của tôi. Tôi vừa thêm nó vào câu trả lời đầu tiên của mình
Francesco Laurita

27
view.getScrollX () và view.getScrollY () luôn trả về 0!
rantravee

3
Hãy xem giải pháp (được chấp nhận) của tôi ở trên bằng cách sử dụng View.getChildAt () và View.getTop (). Bạn không thể sử dụng View.getScrollY () trên ListView vì ListView duy trì cuộn trong nội bộ.
ian

17

Tôi tìm thấy một cái gì đó thú vị về điều này.

Tôi đã thử setSelection và scrolltoXY nhưng nó không hoạt động, danh sách vẫn ở cùng một vị trí, sau một số thử nghiệm và lỗi tôi nhận được mã sau đây không hoạt động

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

Nếu thay vì đăng Runnable, bạn hãy thử runOnUiThread, nó cũng không hoạt động (ít nhất là trên một số thiết bị)

Đây là một cách giải quyết rất kỳ lạ cho một cái gì đó nên được thẳng tiến.


1
Tôi đã trải qua vấn đề tương tự! Và setSelectionFromTop()không hoạt động.
ofavre

+1. listnew.post (), không hoạt động trên một số thiết bị và trên một số thiết bị khác, nó không hoạt động trong một số trường hợp nhất định, nhưng runOnUIThread hoạt động tốt.
Omar Rehman

@Omar, bạn có thể đưa ra một số ví dụ về thiết bị và hệ điều hành mà điều này không hoạt động không? Tôi đã thử một chiếc HTC G2 (2.3.4) và Galaxy Nexus (4.1.2) và nó đã hoạt động trên cả hai. Không có câu trả lời nào khác trong chuỗi này hoạt động cho bất kỳ thiết bị nào của tôi.
Dan J

2
listview.post hoạt động tốt trên Galaxy Note của tôi với 4.0.4, nhưng không hoạt động trên Nexus One của tôi với 2.3.6. Tuy nhiên, onOnUIThread (hành động) hoạt động tốt trên cả hai thiết bị.
Omar Rehman

11

THẬN TRỌNG !! Có một lỗi trong absListView không cho phép onSaveState () hoạt động chính xác nếu ListView.getFirstVisibleP vị trí () bằng 0.

Vì vậy, nếu bạn có hình ảnh lớn chiếm phần lớn màn hình và bạn cuộn sang hình ảnh thứ hai, nhưng một chút hình ảnh đầu tiên đang hiển thị, vị trí cuộn sẽ không được lưu ...

từ absListView.java:1650 (ý kiến ​​của tôi)

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

Nhưng trong tình huống này, 'đỉnh' trong mã bên dưới sẽ là số âm khiến các vấn đề khác ngăn trạng thái được khôi phục chính xác. Vì vậy, khi 'đỉnh' là âm tính, hãy sinh con tiếp theo

// 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);

Nó hoạt động như một bùa mê nhưng tôi không hiểu hết về nó. Nếu mục đầu tiên vẫn còn hiển thị, làm thế nào có ý nghĩa để có được đứa trẻ tiếp theo? Không nên đặtSelectionFromTop với chỉ mục mới khiến danh sách được hiển thị bắt đầu từ con thứ hai? Làm thế nào tôi vẫn nhìn thấy cái đầu tiên?
tafi

@tafi, Mục đầu tiên có thể hiển thị 'một phần'. Vì vậy, phần trên cùng của mục đó sẽ ở phía trên màn hình và do đó sẽ là một số âm. (Điều này gây ra các vấn đề khác mà tôi không nhớ lại ...) Vì vậy, chúng tôi sử dụng 'đỉnh' của Mục thứ hai để đặt, luôn luôn có sẵn (?), Hoặc sẽ không có bất kỳ cuộn nào ở vị trí đầu tiên !
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);
        }
    }
}

Thế là đủ rồi


Xin chào, mã của bạn ở trên có giúp tôi với danh sách RecyclerView trong đó instancestate không được lưu giữa các hoạt động không? Tôi đã đăng câu hỏi sau đây: stackoverflow.com/questions/35413495/ trên ?
AJW

6

Đối với một số người đang tìm kiếm một giải pháp cho vấn đề này, gốc rễ của vấn đề có thể là nơi bạn đang thiết lập bộ điều hợp chế độ xem danh sách của mình. Sau khi bạn đặt bộ điều hợp trên listview, nó sẽ đặt lại vị trí cuộn. Chỉ cần một cái gì đó để xem xét. Tôi đã chuyển cài đặt bộ điều hợp vào onCreateView của mình sau khi chúng tôi lấy tham chiếu đến listview và nó đã giải quyết vấn đề cho tôi. =)


1

Đang đăng bài này vì tôi ngạc nhiên không ai nhắc đến điều này.

Sau khi người dùng nhấp vào nút quay lại, anh ta sẽ trở lại listview trong trạng thái tương tự như khi anh ta rời khỏi nó.

Mã này sẽ ghi đè nút "lên" để hoạt động giống như nút quay lại, trong trường hợp Listview -> Chi tiết -> Quay lại Listview (và không có tùy chọn nào khác) đây là mã đơn giản nhất để duy trì cuộn và nội dung trong danh sách nghe.

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

Thận trọng: Nếu bạn có thể chuyển sang hoạt động khác từ hoạt động chi tiết, nút lên sẽ đưa bạn trở lại hoạt động đó, do đó bạn sẽ phải thao tác lịch sử nút bấm để hoạt động này hoạt động.



1

GIẢI PHÁP TỐT NHẤT LÀ:

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

BẠN PHẢI GỌI VÀO BÀI VIẾT VÀ TRONG THREAD!


1

Bạn có thể duy trì trạng thái cuộn sau khi tải lại nếu bạn lưu trạng thái trước khi tải lại và khôi phục trạng thái sau. Trong trường hợp của tôi, tôi đã thực hiện một yêu cầu mạng không đồng bộ và tải lại danh sách trong một cuộc gọi lại sau khi hoàn thành. Đây là nơi tôi khôi phục lại trạng thái. Mẫu mã là Kotlin.

val state = myList.layoutManager.onSaveInstanceState()

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

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

0

Nếu bạn đang sử dụng các đoạn được lưu trữ trên một hoạt động, bạn có thể làm một cái gì đó như thế này:

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

Nếu bạn đang lưu / khôi phục vị trí cuộn của ListViewchính mình, về cơ bản bạn đang sao chép chức năng đã được triển khai trong khung Android. Việc ListViewkhôi phục vị trí cuộn tốt chỉ tự mình ngoại trừ một cảnh báo: như @aaronvargas đã đề cập, có một lỗi trong AbsListViewđó sẽ không cho phép khôi phục vị trí cuộn tốt cho mục danh sách đầu tiên. Tuy nhiên, cách tốt nhất để khôi phục vị trí cuộn không phải là khôi phục nó. Android framework sẽ làm điều đó tốt hơn cho bạn. Chỉ cần chắc chắn rằng bạn đã đáp ứng các điều kiện sau đây:

  • đảm bảo bạn chưa gọi setSaveEnabled(false)phương thức và chưa đặtandroid:saveEnabled="false" thuộc tính cho danh sách trong tệp bố cục xml
  • cho phương thức ExpandableListViewghi đè long getCombinedChildId(long groupId, long childId)để nó trả về số dài dương (thực hiện mặc định trong lớp BaseExpandableListAdaptertrả về số âm). Dưới đây là ví dụ:

.

@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;
}
  • nếu ListViewhoặc ExpandableListViewđược sử dụng trong một đoạn không tái tạo lại đoạn đó trên hoạt động giải trí (ví dụ sau khi xoay màn hình). Lấy đoạn bằng findFragmentByTag(String tag)phương pháp.
  • đảm bảo ListViewcó một android:idvà nó là duy nhất.

Để tránh cảnh báo đã nói ở trên với mục danh sách đầu tiên, bạn có thể tạo bộ điều hợp của mình theo cách nó trả về chế độ xem chiều cao pixel không giả đặc biệt cho ListViewvị trí 0. Dưới đây là dự án ví dụ đơn giản hiển thị ListViewExpandableListViewkhôi phục vị trí cuộn tốt của chúng trong khi vị trí cuộn của chúng không được lưu rõ ràng / khôi phục. Vị trí cuộn tốt được khôi phục hoàn hảo ngay cả đối với các kịch bản phức tạp với việc chuyển tạm thời sang một số ứng dụng khác, xoay màn hình đôi và chuyển trở lại ứng dụng thử nghiệm. Xin lưu ý, nếu bạn thoát khỏi ứng dụng một cách rõ ràng (bằng cách nhấn nút Quay lại), vị trí cuộn sẽ không được lưu (cũng như tất cả các Chế độ xem khác sẽ không lưu trạng thái của chúng). https://github.com/voromto/RestoreScrollPocation/release


0

Đối với một hoạt động có nguồn gốc từ ListActivity thực hiện LoaderManager.LoaderCallbacks bằng SimpleC bổngAd CHƯƠNG, nó không hoạt động để khôi phục vị trí trong onReset (), vì hoạt động gần như luôn được khởi động lại và bộ điều hợp được tải lại khi chế độ xem chi tiết được đóng lại. Mẹo là khôi phục vị trí trong onLoadFinished ():

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

trong onLoadFinished ():

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

trong onBackPression ():

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

0

Không có giải pháp nào được cung cấp ở đây dường như có tác dụng với tôi. Trong trường hợp của tôi, tôi có một ListViewcái Fragmentmà tôi đang thay thế FragmentTransaction, vì vậy một thể hiện mới Fragmentđược tạo ra mỗi khi đoạn được hiển thị, điều đó có nghĩa là ListViewtrạng thái không thể được lưu trữ như một thành viên củaFragment .

Thay vào đó, tôi đã kết thúc việc lưu trữ trạng thái trong Applicationlớp tùy chỉnh của mình . Mã dưới đây sẽ cho bạn ý tưởng về cách thức hoạt động của nó:

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

}

Ý tưởng cơ bản là bạn lưu trữ trạng thái bên ngoài ví dụ phân đoạn. Nếu bạn không thích ý tưởng có một trường tĩnh trong lớp ứng dụng của mình, tôi đoán bạn có thể thực hiện nó bằng cách triển khai giao diện phân đoạn và lưu trữ trạng thái trong hoạt động của bạn.

Một giải pháp khác là lưu trữ nó SharedPreferences, nhưng nó phức tạp hơn một chút và bạn cần chắc chắn rằng bạn đã xóa nó khi khởi chạy ứng dụng trừ khi bạn muốn trạng thái được duy trì trong suốt quá trình khởi chạy ứng dụng.

 

Ngoài ra, để tránh "vị trí cuộn không được lưu khi hiển thị mục đầu tiên", bạn có thể hiển thị mục đầu tiên giả có 0pxchiều cao. Điều này có thể đạt được bằng cách ghi đè getView()trong bộ điều hợp của bạn, như thế này:

@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

Câu trả lời của tôi là dành cho Firebase và vị trí 0 là một cách giải quyết

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

và convertView

vị trí 0 a thêm một mục trống mà bạn không sử dụng

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

Tôi đã thấy công việc này tạm thời mà không làm phiền người dùng, tôi hy vọng nó hoạt động cho bạn


0

sử dụng mã dưới đây:

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

và bất cứ khi nào bạn làm mới dữ liệu của mình, hãy sử dụng mã dưới đây:

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

0

Tôi đang sử dụng FirebaseListAdOG và không thể có bất kỳ giải pháp nào hoạt động. Tôi đã kết thúc việc này. Tôi đoán có nhiều cách thanh lịch hơn nhưng đây là một giải pháp hoàn chỉnh và hiệu quả.

Trước khi tạo:

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

Bên trong FirebaseListAd CHƯƠNG:

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

 }

băt đâu:

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

dừng lại:

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

Vì tôi cũng phải giải quyết vấn đề này cho FirebaseRecyclerAd CHƯƠNG Tôi cũng đang đăng giải pháp ở đây cho điều đó:

Trước khi tạo:

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

Bên trong FirebaseRecyclerAd CHƯƠNG:

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

băt đâu:

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

dừng lại:

@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

Để làm rõ câu trả lời xuất sắc của Ryan Newsom và điều chỉnh nó cho các đoạn và trong trường hợp thông thường mà chúng tôi muốn điều hướng từ một đoạn ListView "chính" đến một đoạn "chi tiết" và sau đó quay lại "chủ"

    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
    }

"Phép thuật" ở đây là khi chúng ta điều hướng trở lại từ đoạn chi tiết đến đoạn ListView, chế độ xem không được tạo lại, chúng ta không đặt bộ điều hợp của ListView, vì vậy mọi thứ vẫn còn khi chúng ta rời khỏi nó!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.