android - ListView पर लौटने पर स्क्रॉल स्थिति को बनाए रखें/सहेजें/पुनर्स्थापित करें




android-listview scroll (11)

ListActivity से व्युत्पन्न गतिविधि के लिए जो LoaderManager.LoaderCallbacks को सरल CursorAdapter का उपयोग करके लागू करता है, यह स्थिति रीसेट () में स्थिति को पुनर्स्थापित करने के लिए काम नहीं करता है, क्योंकि गतिविधि लगभग हमेशा पुनरारंभ होती है और विवरण दृश्य बंद होने पर एडाप्टर को पुनः लोड किया गया था। यह चाल ऑनलोड पर स्थिति को पुनर्स्थापित करना था ():

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

ऑनलोड लोड ():

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

पर बैकप्रेस ():

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

मेरे पास एक लंबी ListView कि उपयोगकर्ता पिछली स्क्रीन पर लौटने से पहले चारों ओर स्क्रॉल कर सकता है। जब उपयोगकर्ता फिर से इस ListView खोलता है, तो मैं चाहता हूं कि सूची उसी बिंदु पर स्क्रॉल हो जाए जो पहले था। इसे कैसे प्राप्त किया जाए इस पर कोई विचार?


इसे इस्तेमाल करे:

// 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 के शीर्ष से अपने शीर्ष किनारे को स्थानांतरित करने के लिए कहते हैं।


कुछ लोगों के लिए इस समस्या का हल ढूंढने के लिए, समस्या की जड़ हो सकती है जहां आप अपनी सूची दृश्य एडाप्टर सेट कर रहे हैं। सूचीदृश्य पर एडाप्टर सेट करने के बाद, यह स्क्रॉल स्थिति रीसेट करता है। बस कुछ विचार करने के लिए। सूची सूची के संदर्भ को पकड़ने के बाद मैंने एडाप्टर को अपने ऑनक्रेट व्यू में स्थानांतरित कर दिया, और यह मेरे लिए समस्या हल हो गई। =)


बस android:saveEnabled="true" नहीं है android:saveEnabled="true" ListView xml घोषणा में पर्याप्त है?


मैं इसे पोस्ट कर रहा हूं क्योंकि मुझे आश्चर्य है कि किसी ने इसका उल्लेख नहीं किया था।

उपयोगकर्ता बैक बटन पर क्लिक करने के बाद वह उसी स्थिति में सूचीदृश्य पर वापस आ जाएगा क्योंकि वह इससे बाहर निकल गया था।

यह कोड "अप" बटन को बैक बटन के समान व्यवहार करने के लिए ओवरराइड करेगा ताकि सूचीदृश्य के मामले में -> विवरण -> सूची में वापस जाएं (और कोई अन्य विकल्प नहीं) स्क्रॉलपॉज़िशन और सामग्री को बनाए रखने के लिए यह सबसे आसान कोड है सूचीदृश्य में।

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

सावधानी: यदि आप विवरण गतिविधि से दूसरी गतिविधि में जा सकते हैं तो ऊपर बटन आपको उस गतिविधि पर वापस कर देगा ताकि आपको काम करने के लिए बैकबटन इतिहास में हेरफेर करना पड़े।


मैंने @ (किर्क वोल) द्वारा सुझाए गए समाधान को अपनाया, और यह मेरे लिए काम करता है। मैंने "संपर्क" ऐप के लिए एंड्रॉइड स्रोत कोड में भी देखा है, कि वे एक समान तकनीक का उपयोग करते हैं। मैं कुछ और विवरण जोड़ना चाहता हूं: मेरी सूची एक्टिविटी-व्युत्पन्न कक्षा पर शीर्ष पर:

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" डीबी से डेटा पुनर्प्राप्त करने और इसे सूची में रखने के लिए मेरा कार्य है।

मेरे फ्रायओ डिवाइस पर, यह तब होता है जब आप फ़ोन अभिविन्यास बदलते हैं, और जब आप कोई आइटम संपादित करते हैं और सूची में वापस जाते हैं।


यदि आप किसी गतिविधि पर होस्ट किए गए टुकड़ों का उपयोग कर रहे हैं तो आप ऐसा कुछ कर सकते हैं:

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

यदि आप पुनः लोड करने से पहले राज्य को सहेजते हैं और इसे पुनर्स्थापित करने से पहले आप रीलोड के बाद स्क्रॉल स्थिति को बनाए रख सकते हैं। मेरे मामले में मैंने एक एसिंक्रोनस नेटवर्क अनुरोध किया और इसे पूरा होने के बाद कॉलबैक में सूची को फिर से लोड किया। यह वह जगह है जहां मैं राज्य बहाल करता हूं। कोड नमूना Kotlin है।

val state = myList.layoutManager.onSaveInstanceState()

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

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

सावधान!! AbsListView में एक बग है जो 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);

सर्वोत्तम समाधान है:

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

आपको पोस्ट में और थ्रेड में कॉल करना होगा!


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

बस काफी है






scroll-position