Restore Recycler View state on device's rotation - android

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).

Related

RecyclerView Position on Screen Rotation with Refresh

I have simple Fragment and I use RecylerView to list my elements. I want to keep scroll position when I change screen rotation.
Regarding the answer here I added these lines to my Fragment code;
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("position", recyclerView.getVerticalScrollbarPosition());
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState != null) {
recyclerView.scrollToPosition(savedInstanceState.getInt("position"));
}
}
But I have a special case here. Because I have to refresh my adapter when I rotate my screen. I use these lines to refresh:
#Override
public void onConfigurationChanged(#NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
refreshAdapter();
}
private void refreshAdapter() {
if (adapter != null) {
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
adapter.notifyDataSetChanged();
}
}
When I comment out refreshAdapter(); line, I can keep my scroll position. But I have to use refreshAdapter(); line.
How can I keep my scroll position with these methods ?

RecyclerView Adapter not updating views after device rotation

I have a chat app, which opens a Websocket to a server. The app has 2 modules -
chat sdk: which handles the connection plus back and forth
messaging.
ui: which handles adapters and views
The chat UI is on a fragment hosted by an activity.
Everything worked fine but when i decided to use persistence on device rotation, i have problems.
When i rotate the device, the websocket reconnects (which is normal),the activity is recreated so is the fragment too.
After device rotation and some debugging, i saw that my messageList is properly filled with any added items (ie message texts) but not the UI. When i type a text from the EditText and press send, the item is properly displayed in the UI, BUT when i receive response from server the UI is not updated even if the message is added to the messageList.
I read in similar posts that the RecyclerView adapter is updating the old fragment after the rotation. Is this the case? And if this is the case, why when i send a message from the EditText it is properly displayed but not when from the onMessage of my WebSocket?
I tried "android:configChanges="screenSize|orientation|screenLayout" w/o any results.
I override the onConfigurationChanged and force the adapter to update the views, with no result
#Override
public void onConfigurationChanged(#NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if(messagesAdapter!=null){
messagesAdapter.notifyDataSetChanged();
}
}
I save the fragment to restore it in my activity
public class MessageActivity extends AppCompatActivity {
private MessageFragment fragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_message);
if (savedInstanceState == null) {
fragment = MessageFragment.newInstance();
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, fragment)
.commitNow();
}else{
fragment = (MessageFragment) getSupportFragmentManager().getFragment(savedInstanceState, "messageFragment");
}
}
#Override
public void onBackPressed() {
finishAffinity();
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Save the fragment's instance
getSupportFragmentManager().putFragment(outState, "messageFragment", fragment);
}}
And i also save and retrieve my messageList
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
//saving the state of the chat
outState.putParcelableArrayList("messages", messageArrayList);
try {
Log.d(TAG, "messagelist in save instance " + messageArrayList.size());
}catch (NullPointerException e){
e.printStackTrace();
}
outState.putString("dialogId", messageArrayList.get(0).getDialogId());
}
onMessage from websocket
#Override
public void onMessage(ArrayList<Message> data) {
if (!messageArrayList.isEmpty() && messageArrayList.get(messageArrayList.size() - 1).getServerMessageType() == Constants.TYPE_TYPING_ON) {
messageArrayList.remove(messageArrayList.size() - 1);
}
this.messageArrayList.addAll(data);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && isFragmentAttached) {
mActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
if (omiliaClient != null) {
updateUi(messageArrayList, omiliaClient.user2);
}
}
});
}
}
Call setRetainInstance(true) in either of onCreate(), onCreateView() or onActivityCreated() methods.

Store/restore state of RecyclerView in Fragment between activities

In one of my activities I have two fragments in a ViewPager. Now I'm trying to save the RecyclerView's state for one of them using this code:
public void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
mListState = layoutManager.onSaveInstanceState();
state.putParcelable("LIST_STATE_KEY", mListState);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (state != null) {
mListState = state.getParcelable("LIST_STATE_KEY");
if (mListState != null) {
layoutManager.onRestoreInstanceState(mListState);
}
}
}
This code doesn't work for me and I'm trying to find out how I can develop that to resolve this problem.

Orientation: restore ListView position that is inside a Fragment

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.

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