I have slight problem. I have gridView in main activity, and on device rotation the list will always start from the top. I fonud several solutions but non of them work for gridView. How can I maintain same gridView position on device rotation using onSavedInstanceState?
Thanks in advance. :)
You first need to understand the facts because of which this is happening. This is happening because whenever your device orientation changes your onDestroy() and onCreate() lifecycle methods are called. To survive your activity state using onSaveInstanceState, you need to save your grid view scroll position when onDestroy() is called and then retrieve it in your onCreate() method. You can do this using Bundle. The code will look something like this, onSaveInstanceState() will be called by android os before your activity get destroyed \n
#Override
onSaveInstanceState(Bundle bundle){
super.onSaveInstanceState(bundle);
bundle.putInt("SCROLL_VIEW_POSITION",scroll_position);
}
//And in your on Create
onCreate(Bundle saveInstanceState){
super.onCreate(saveInstanceState);
int scrollPosition = saveInstanceState.get("SCROLL_VIEW_POSITION",default_value);
}
Once you get your same scrollPosition before oreientation change you can use it to set scroll position of your grid view.
Related
I'm making a manga application but I stuck with save current scroll position when user rotate their device (landscape to portrait or portrait to landscape). I'm using RecyclerView and GridLayoutManager. Here is two block code that I have researched on the internet:
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
rvItems.getLayoutManager().scrollToPosition(savedInstanceState.getInt("position"));
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt("position", ((GridLayoutManager)rvItems.getLayoutManager()).findFirstCompletelyVisibleItemPosition());
super.onSaveInstanceState(outState);
}
I'm using it and either in onResume() or onPause(), none of them are working. So I'm asking for any idea or suggestion that solve my problem? Any comment will be approriate.
P/S: One more problems, Is there any solution for stop loading all resource/data in current activity while user rotates their screen? I mean in my application, there might be numerous data and resource either text and images, as I know, every time user rotate their screen, the function onCreate() and onDestroy() was called which every data in onCreate() function will be loaded again. I need the resource/data only load one time since it's opened, then it won't load resource when user rotates their screen.
My writing might be very bad but now I'm sticking with many problems without any solution I can think or search in internet.
Thanks for reading!
add this to your activity tag in manifest
android:configChanges="orientation"
I have an activity MainActivity there are three fragments associated with this activity.
Now one of my fragment Timeline has a listview. Which I populate from a Database in the backend. I use an AsyncTask to fetch values from the DB and process them to the List. I trigger this AsyncTask in the onCreate of the Fragment Timeline.
Now from Timeline on click of any list item I navigate to a different Activity called as DetailActivity
The problem is whenever I press back from the DetailActivity the onCreate of my MainActivity is called and my list refreshes again - the whole DB operation is called again and my list does not retain its state.
I am calculating the visible items of my List before I navigate away from the Fragment but I am forced to use static values for these variables so that I retain the position. How to avoid this?
Below are the snippets of my onPause and onResume as laid down in the fragment Timeline
static int index;
static int top;
#Override
public void onPause(){
System.out.println("onPause");
index = lv.getFirstVisiblePosition();
View v = lv.getChildAt(0);
top = (v == null) ? 0 : v.getTop();
super.onPause();
uiHelper.onPause();
}
#Override
public void onResume(){
super.onResume();
//dbHelper.open();
System.out.println("onResumr");
lv.setSelectionFromTop(index, top);
ActionBar actionBar = getActivity().getActionBar();
actionBar.setTitle("Timeline");
uiHelper.onResume();
AppEventsLogger.activateApp(getActivity());
updateUI();
}
This also forces my AsyncTask to run again and again, which is an overhead.
Edit:
The root of this problem - After struggling for so many days I borrowed a friends phone to test and all was sorted on this new phone. I found out that I had turned on the Do not keep Activities option in my Developer Settings. The Dumb me!!
This is, unfortunately, the default behavior of the Fragment class. A Fragment is destroyed whenever the containing Activity is paused, and recreated whenever the containing Activity is resumed. If you use an Activity instead of a Fragment for the list, you would not experience the same behavior. With an Activity:
AsyncTasks and/or web services would not be called again.
The list would show the previously scrolled position.
If you want the same behavior with a Fragment, you need to override the onSaveInstanceState() method. And while interesting, it is not a small amount of work.
EDIT:
Make sure the Do not keep Activities option is unselected in your phone's Developer Settings. This, though, does not change the essential behavior of the Fragment class that I have outlined above.
You can call setRetainInstance(true) on your fragment. The lifecycle will be slightly different though.
A nice view of a fragment's lifecycle is available here
http://corner.squareup.com/2014/10/advocating-against-android-fragments.html
if the question sounds weird at first, here comes the explanation:
I have got an activity that hosts my three fragments. Since I would like one of my fragments to save its instance state when the device is rotated, I defined this in my manifest for my activity that hosts the fragments:
android:configChanges="orientation|screenSize"
This works just fine. However, now I have got an other problem: One of my other fragments uses a special landscape layout. The problem is, that this layout is not used immediately on device rotation. I think it is because the new layout only gets set on onCreate.
What can I do to solve this problem? I want my landscape layout to be set immediately.
You can put
setRetainInstance(true);
in onCreateView(); method of your Fragment. I think it should do the trick.
As far as I know you down need to add the configChanges parameter to your manifest.
You can override onSaveInstanceState() in your Fragment
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putInt(KEY_INDEX, someIntValue);
}
This methode should be called before your fragment gets destroyed.
Now in your onCreate(Bundle savedInstanceState) (or onCreateView()) methode:
if (savedInstanceState != null) {
someIntValue = savedInstanceState.getInt(KEY_INDEX);
}
This way it shouldn't intervene with any other special fragments.
I have an Activity with ActionBarSherlock tabs and a ViewPager inside it. When the pages are scrolled, the tabs are switched and tabs are changed, the current page is changed too.
I am using a class that extends FragmentStatePagerAdapter as adapter in the pageview.
The problem is that when the device rotates, the getItem from the page adapter is not called and looks like the fragments references are not right.
That's a huge problem, since the user must fulfill some fields inside the pager. These fields are recovered in correctly on the rotation, but since I the references for the fragments are not right, I can't save these values in right way.
Any idea about the rotation?
Save the current page number in onSavedInstanceState:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("item", mViewPager.getCurrentItem());
}
Then in onCreate:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState != null) {
mViewPager.setCurrentItem(savedInstanceState.getInt("item"));
}
}
Nothing solved, created an issue for AOSP:
http://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars&groupby=&sort=&id=54823
I've posted a solution to this problem in the question here https://stackoverflow.com/a/21517213/3170538. Essentially you want to be subclassing PagerAdapter, not FragmentPagerAdapter, and handling the creating and deleting of items yourself (getItem() is a routine that is called from FragmentPagerAdapter.instantiateItem(), so the issue is when the screen is rotated the routine doesn't re-call getItem(), because of some presumably performance-enhancing code. When you subclass it you can handle the rotating properly by not trying to reconnect to the deleted fragment).
According to the documentation of onSaveInstanceState:
The default implementation takes care of most of the UI per-instance state
and onRestoreInstanceState:
The default implementation of this method performs a restore of any view state that had previously been frozen
I'm not sure exactly what that means.
Is it meant to mean that when returning after being killed and now restored, that the UI screen shown to the user is automatically restored with all its data?
If so, I am not seeing that.
All I get is an empty screen unless I do setContentView myself.
AM I misunderstanding the meaning?
Default implementation will work for every widget which ids are defined.
For example, If you have one EditText and if you will provide its id then system will save its value when Activity will be killed due to orientation and same and it will restore the EditText value when activity will be re-created.
Edit
If you have one base layout and if you are dynamically adding some views in the view hierarchy then you will have to handle the save state and restore state your self. also when your activity will be re-created then onCreate() method of the activity will be called so in this method first set all the addition views which you are creating and adding dynamically and then you can check the extra parameters with the intent which you are getting in the onCreate() method. This extra parameters are exactly same as you have adding extra parameters in the onSaveInstanceState method.
So implement like below.
int x = 10;
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("x", x);
}
And in onCreate method you can get this x parameters like below
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.xxx);
if(savedInstanceState.containsKey("x")) {
x = savedInstanceState.getInt("x");
}
}