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);
}
Related
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 ?
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.
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.
I have fragment contains recyclerview in main activity
Fragment.java
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View view = inflater.inflate(R.layout.activity_main, container, false);
initInstances(view);
setRetainInstance(true);
return view;
}
private void initInstances(View view) {
mRecyclerView = (RecyclerView) view.findViewById(R.id.dialogList);
mLayoutManager = new LinearLayoutManager(getActivity());
mRecyclerView.setLayoutManager(mLayoutManager);
adapter = new MyAdapter(items);
mRecyclerView.setAdapter(mAdapter);
}
MainActivity.java
in onCreate method:
myFragment = (MyFragment) getSupportFragmentManager().findFragmentByTag(MyFragment.TAG);
if (MyFragment == null) {
MyFragment = new MyFragment();
getSupportFragmentManager().beginTransaction().add(R.id.content_frame,
myFragment, MyFragment.TAG).commit();
}
But when orientation changed, my RecyclerView redrawn.
I've tried save state with overriding onSaveInstanceState and onRestoreInstanceState like this How to prevent custom views from losing state across screen orientation changes or Saving and restoring view state android, but to no avail for me
How to correctly save state of RecyclerView and adapter's items when orientation change?
SOLUTION:
My fragment extends from BaseFragment with some methods:
public class BaseFragment extends Fragment{
Bundle savedState;
public BaseFragment() {
super();
if (getArguments() == null)
setArguments(new Bundle());
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (!restoreStateFromArguments()) {
onFirstTimeLaunched();
}
}
protected void onFirstTimeLaunched() {
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
saveStateToArguments();
}
public void saveStateToArguments() {
if (getView() != null)
savedState = saveState();
if (savedState != null) {
Bundle b = getArguments();
b.putBundle("internalSavedViewState8954201239547", savedState);
}
}
private boolean restoreStateFromArguments() {
Bundle b = getArguments();
savedState = b.getBundle("internalSavedViewState8954201239547");
if (savedState != null) {
onRestoreState(savedState);
return true;
}
return false;
}
private Bundle saveState() {
Bundle state = new Bundle();
onSaveState(state);
return state;
}
protected void onRestoreState(Bundle savedInstanceState) {
}
protected void onSaveState(Bundle outState) {
}
#Override
public void onStart() {
super.onStart();
}
#Override
public void onStop() {
super.onStop();
}
#Override
public void onDestroyView() {
super.onDestroyView();
saveStateToArguments();
}
In my fragment I override methods and save state of mLayoutManager
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
protected void onSaveState(Bundle outState) {
super.onSaveState(outState);
outState.putParcelable("myState", mLayoutManager.onSaveInstanceState());
}
#Override
protected void onRestoreState(Bundle savedInstanceState) {
super.onRestoreState(savedInstanceState);
mLayoutManager.onRestoreInstanceState(savedInstanceState.getParcelable("myState"));
}
I searched how to get the scroll position of the RecyclerView, but didn't see where I can retrieve it, so I think the best solution is to handle rotation changes directly in your java code, and preventing your activity to restart on orientation change.
To prevent restarting on orientation change, you can follow this solution.
After preventing your Activity from restart, you can build 2 xml layout files, one for portrait mode, and one for landscape mode, you add a FrameLayout in both xml files, holding the place for your RecyclerView.
When the onConfigurationChange(Configuration newConfig) method is called, you call setContentView(int layoutFile) with the appropriate LayoutFile following the new orientation, and add your RecyclerView you are holding in a member of your class in the FrameLayout.
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);
}
});