Orientation: restore ListView position that is inside a Fragment - android

I went trough a famous post in SO, Maintain/Save restore scroll position
but does not help me at all.
I have a ListView inside a Fragment, if I change the orientation, I would like that saveInstance Bundle will save my position.
I have
private static final String LIST_STATE = "listState";
private Parcelable mListState = null;
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mListState = listView.onSaveInstanceState();
outState.putParcelable(LIST_STATE, mListState);
}
and does not matter if I put in OnCreateView or in onActivityCreated the following code
if(savedInstanceState!=null) {
mListState = savedInstanceState.getParcelable(LIST_STATE);
listView.onRestoreInstanceState(mListState);
the up position of the list is not restored at all.
I can easily see from the debug in Bundle that the debug recognize in the Bundle the position,
AbsListView.SavedState{3d7562e0 selectedId=-9223372036854775808
firstId=25 viewTop=-38 position=5 height=717 filter=null
checkState=null}
I even tried to extract from the Bundle this position in an isolated way instead of all the values, but without success.

add following code inside fragment
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}

Found it!
The solution is
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mPosition2=listView.getFirstVisiblePosition();
if(mPosition2!=0) {
mPosition = mPosition2;
}
if (mPosition != ListView.INVALID_POSITION) {
outState.putInt(SELECTED_KEY, mPosition);
}
}
and in onCreateView
if (savedInstanceState != null && savedInstanceState.containsKey(SELECTED_KEY)) {
mPosition = savedInstanceState.getInt(SELECTED_KEY);
}
finally I have a loader that query a content provider at end of onLoadFinished( but you can put you just where you need)
if (mPosition != ListView.INVALID_POSITION) {
listView.setSelection(mPosition);
I have also tried listView.smoothScrollToPosition(mPosition)
but is not working at the moment, but never mind it works really well to me.

Related

Recyclerview in fragment not loading after screen rotate

I want to save my fragment with recycler view, to restore it whlie screen rotate. I was looking for instructions in the net and I get:
In my Fragment
private Parcelable recyclerViewState;
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
list.getLayoutManager().onRestoreInstanceState(recyclerViewState);
}
}
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
recyclerViewState = layoutManager.onSaveInstanceState();
}
in my Activity
//onCreate
if (savedInstanceState != null) {
getSupportFragmentManager().getFragment(savedInstanceState, LIST_USERS_FRAGMENT);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
getSupportFragmentManager().getFragment(outState, LIST_USERS_FRAGMENT);
}
I was debbuging and it looks fine, however while I rotate my screen I get blank fragment, with no recycler view. Could You help me investigate why it's happening ?
The default behavior in Android is destroying all the attached fragments on configuration changes. To bypass fragment recreation you should use setRetainInstance(true) in the OnCreate callback of the fragment.
There are a different options to handle the orientation changes:
Lock screen orientation
<activity
android:name="com.example.test.activity.MainActivity"
android:screenOrientation="portrait"/>
Prevent Activity to recreated
<activity
android:name="com.example.test.activity.MainActivity"
android:configChanges="orientation|screenSize|keyboardHidden"/>
Save basic state
public class MainActivity extends Activity{
private static final String SELECTED_ITEM_POSITION = "ItemPosition";
private int mPosition;
#Override
protected void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
// Save the state of item position
outState.putInt(SELECTED_ITEM_POSITION, mPosition);
}
#Override
protected void onRestoreInstanceState(final Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Read the state of item position
mPosition = savedInstanceState.gettInt(SELECTED_ITEM_POSITION);
}
}
Save complex objects
Override
onRetainNonConfigurationInstance()
getLastNonConfigurationInstance()
After API level 13 these methods have been deprecated in favor of the more Fragment’s setRetainInstance(boolean) capability, which provides a much cleaner and modular means of retaining objects during configuration changes.

Restore Recycler View state on device's rotation

I have RecyclerView which views are populated from the internet (page number is a parameter) and that is why I used the EndlessRecyclerViewScrollListener. The only problem is that when I rotate the device, it doesn't restore the same state.
This is my ScrollListener initiation in onCreate():
mScrollListener = new EndlessRecyclerViewScrollListener(mGridLayoutManager) {
#Override
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
mPage++;
new TheMovieRequestTask().execute();
}
};
mRecyclerView.addOnScrollListener(mScrollListener);
onSaveInstanceState():
protected void onSaveInstanceState(Bundle outState) {
outState.putInt("mPageKey", mPage);
mListState = mRecyclerView.getLayoutManager().onSaveInstanceState();
outState.putParcelable("mListStateKey", mListState);
super.onSaveInstanceState(outState);
}
At the beginning of onCreate() I have:
if (savedInstanceState != null) {
mPage = savedInstanceState.getInt("mPageKey");
mBundleRecyclerViewState = new Bundle();
mListState = savedInstanceState.getParcelable("mListStateKey");
mBundleRecyclerViewState.putParcelable("mListStateBundleKey", mListState);
} else {
mPage = 1;
}
And when I load data from TheMovieRequestTask() (it's an AsyncTask), in onPostExecute() i have:
if (mBundleRecyclerViewState != null) {
Parcelable state = mBundleRecyclerViewState.getParcelable("mListStateBundleKey");
mRecyclerView.getLayoutManager().onRestoreInstanceState(state);
}
What Can I do? When I go back from another activity, it's perfect but on rotation, it just doesn't work.
This code loads e.g. 5th page and then properly restore it but then I can scroll only down (6,7,8,9... pages) but not up(not 4,3,2,1, pages).

Not reload recyclerview after screen rotation

I would like to not reload the RecyclerView after screen rotation from portrait to landscape.
I'm using onSaveInstanceState to save list from adapter, and restore it inside onActivityCreated
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if(mAdapter != null) {
outState.putParcelableArrayList("key", mAdapter.getOriginalList());
}
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(savedInstanceState != null) {
mList = savedInstanceState.getParcelableArrayList("key");
mAdapter.notifyDataSetChanged();
}
}
But there is problem, the RecyclerView is reconstructed after each screen rotation. What is wrong ?
You need to set adapter on configuration changed programetically then only it can be possible to set data without reloading.
Something like below,
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.layout.main);
if(mAdapter != null)
your_recycle_view.setAdapter(mAdapter);
}

Is it save to overrwrite the savedInstanceState bundle after passing it up?

I have the following (to avoid checking if a fragment is being created newly or just being recreated)
#Override public void onActivityCreated(Bundle args) {
super.onActivityCreated(args);
if (this.getArguments() != null) args = this.getArguments();
// rest of fragment loading goes here
}
Not sure what you are trying to do never seen budle values being overwritten.
If you want some values to be availbale inside fragment pass those values to activity and inside fragment call getActivity().getIntent().getExtra(
Otherwise have a look at code below if want save some fragment data if fragment is destroyed
Inside fragment in onCreate method check if savedInstanceState is null or not
#Override
public void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) {
//fragment recreated
}
}
Save data as follows
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (jsonObject != null) {
outState.putString(Constants.JSON, jsonObject.toString());
}
}

how to set listview's position after configuration change

I'm struggling to recover my position in a listview on screen rotation configuration change.
Amongst the many things I've tried I came to this:
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState == null) {
...
mVisibleItem = -1;
} else {
if (savedInstanceState.containsKey(LV_VISIBLE_ITEM)) {
mVisibleItem = savedInstanceState.getInt(LV_VISIBLE_ITEM);
}
}
setRetainInstance(true);
}
and here I'm trying to set the position in the listview
#Override
public void onResume() {
super.onResume();
if (mVisibleItem > 0) {
mlvDictionaryIndex.setSelectionFromTop(mVisibleItem, 0);
}
}
However, much to my surprise, after rotating the screen and watching mVisibleItem gets set with the correct value, in onResume I see that mVisibleItem equals -1. How come?
use onSavedInstanceState to write in the bundle the returned value of ListView.onSaveInstanceState(), and restored it onActivityCreated
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mListView != null) {
outState.putParcelable(LISTVIEW_INTERNAL_STATE_KEY, mListView.onSaveInstanceState());
}
}
after the data are reload then you can call
mListView.onRestoreInstanceState(savedInstanceState.getParcelable(LISTVIEW_INTERNAL_STATE_KEY));
Override onSaveInstanceState such as below"
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putInt("pos", pos);
}
Then in your onCreate method have read the savedInstanceState to check if this is an orientation change or a new activity.
private int pos = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
//This is a new activity
}else{
pos = savedInstanceState.getInt("");
}
Now you have the position in the list, and you can scroll to this in configuration change.
Maybe after the data in the listview is reloaded,the code below will work.
mlvDictionaryIndex.setSelectionFromTop(mVisibleItem, 0);
So you can use post() method, just like below:
post(new Runnable() {
public void run() {
mlvDictionaryIndex.setSelectionFromTop(mVisibleItem, 0);
}
});

Categories

Resources