I have statically created a fragment (via XML). I'm trying to store the last displayed value in a bundle and display it whenever the app is started next. However I am not able to get it to work. For some reason savedInstanceState is always null.
public class DistanceSetterFragment extends Fragment implements SharedPreferences.OnSharedPreferenceChangeListener {
Distance distance = new Distance();
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (savedInstanceState!=null )
{
Log.d(this.getClass().getName(),"onCreate savedInstanceState is NOT null");
}
else
{
Log.d(this.getClass().getName(),"onCreate savedInstanceState is null");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
Log.d(this.getClass().getName(),"Distance "+distance);
if (savedInstanceState!=null )
{
Log.d(this.getClass().getName(),"onCreateView savedInstanceState is NOT null");
}
else
{
Log.d(this.getClass().getName(),"onCreateView savedInstanceState is null");
}
return inflater.inflate(R.layout.fragment_distancesetter, container, false);
}
#Override
public void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
if (distance!=null) {
Log.d(this.getClass().getName(),"Saving DISTANCE_BEAN "+distance);
outState.putSerializable(Constants.DISTANCE_BEAN, distance);
}
else
{
Log.d(this.getClass().getName(),"Distance BEAN IS NULL");
}
outState.putString("", "");
}
}
Below is the fragment XML declared in my main activity XML
<fragment
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/fragment_distancesetter"
android:layout_below="#id/img_logo_main"
android:name="com.webconfs.xyz.fragments.DistanceSetterFragment"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
/>
As you can see
- I have NOT set setRetainInstance(true) in my Fragment class and
- My fragment XML has an ID associated with it
android:id="#+id/fragment_distancesetter
I had the same problem just now and it was driving me crazy, but as it turns out the fragment was simply being re-added in the activity with every rotation. You didn't add your Activity's code, but you might want to check this is not the case as it's easy to overlook and would explain your problem.
When you put a fragment into an activity statically, the fragment manager will always create a new fragment and attach it to activity. The restoreInstanceState() method will never be called. If you want to do it, you can save state in your activity's restore method, and put save state of your activity to your fragment. Or create fragment dynamically.
Related
Recently i create an application that execute an async task, my app has a toolbar that contains an sliding tabs, when i change my tab from first tab to second tab my async task execute over and over again, how can i avoid that?, how can i save the state of my fragment? i tried to use
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
if(savedInstaceState!=null){
//TODO
}
}
You are correct with that you have to use the save instance state..in your case you can store the data into a singleton class or a static property in the main activity, then in your fragment you create a global boolean isDataLoaded, which in case of true value you dont execute the task. to save the state of the variable just do :
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putBoolean("LOADDATA", isDataLoaded);
super.onSaveInstanceState(outState);
}
Then in your createView :
if(savedInstanceState != null){
isDataLoaded = savedInstanceState.getBoolean("LOADDATA");
}
I have a few fragments that are loaded when a user clicks on an item in a list. Say a user has clicked on second item in the list, loading the second fragment. But, upon rotating, the screen, the first fragment in the list gets loaded. How can I make sure that the same fragment gets loaded whenever a user rotates the screen.
This is how I'm loading my fragments
private void selectItem(position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = new FirstFragment();
break;
case 1:
fragment = new SecondFragment();
break;
case 2:
fragment = new ThirdFragment();
break;
default:
break;
}
if (fragment != null) {
android.app.FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
}
else {
Log.e("NavigationActivity", "Error in creating fragment");
}
}
I'm calling selectItem(0) in onCreate of an activity.
The entire activity gets destroyed and recreated during a rotation. So if you are calling setItem(0) in Activity.onCreate, then you'll always get FirstFragment in the content frame.
Seems like the easy thing may be to just detect if you've already set a fragment in onCreate and not load the default. Either make use of onSaveInstanceState and/or mark the fragment as retained.
I don't have much experience with retained fragments or fragment management beyond initial load, so just using onSaveInstanceState to keep track of which one was loaded seems appropriate.
In your Activity, override onSaveInstanceState:
#Override
public void onSaveInstanceState(Bundle bundle)
{
bundle.putInt("which_fragment", _fragmentId);
super.onSaveInstanceState(bundle);
}
Where _fragmentId is just some numerical identifier of the particular fragment you are loading. It could even be it's layout id. Set this value in your selectItem method.
And then in onCreate:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
_fragmentId = 0;
if (savedInstanceState != null)
{
_fragmentId = savedInstanceState.getInt("which_fragment", 0);
}
...
selectItem(_fragmentId);
}
First of all I wouldn't use positionOnTheList->Fragment dependency. I would depend on some id (final or from the resources).
Secondly I think you shouldn't create a new instance of each Fragment class when you select item from the list.
You should consider this approach:
Fragment f = fragmentManager.findFragmentById( String.valueOf(id) );
if( f == null )
f = new FragmentDependingOnId();
mCurrentlySelectedId = id;
fragmentManager.beginTransaction()
.replace( R.id.container, f , String.valueOf(id))
.commit();
Add the following method:
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(SELECTED_ID, mCurrentlySelectedId);
}
and in onCreate add:
if(savedInstanceState!=null){
mCurrentlySelectedId = savedInstanceState.getInt(SELECTED_ID);
selectItem(mCurrentlySelectedId);
}
When using fragment you usually use onCreateView to inflate your layout. Then you use onActivityCreated to do all the stuff you need to init listviews etc ...
In your case the problem you have is that you should use the saveInstanceState to keep track of if a fragment is loaded or not because the fragment is re-created on each rotation.
Let's look at some code
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.your_fragment_layout, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
//do nothing if the state already exists
} else {
//do something if state already exists
}
}
Note that if you need to save a given value, for example a boolean you can use
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(YOUR_BOOL_TAG, mYourBooleanVar);
}
and get it back in the onCreateView by using
mYourBooleanVar= savedInstanceState.getBoolean(YOUR_BOOL_TAG);
same applies to other types also.
EDIT
I didn't quite answered your question, so I put more details. The above code is in the fragment. However for your question, in the activity you need something like that.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
//here is the transaction to load your first fragment
}
}
and your first fragement won't reload each time.
The first time savedInstanceState will be null and you set your default fragment. Then each time you rotate savedInstanceState is not null and your default fragment is not reloaded but the one that is currently present.
Only this code is relevant for you, but I let the code above the EDIT for other people in case it can be useful to them.
I have a fragment that seems to be automatically restoring state over a screen rotation configuration change. I can verify in the logs that onCreatView is called in the fragment whenever the screen is rotated. Despite calling down to applyDefaults(), the draft entries that the user made are retained when the screen rotates and onCreateView() is called.
My understanding is that I would have to save state in onSaveInstanceState() and restore it in onCreateView(), but that doesn't seem to be the case. Can someone explain?
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG, "onCreateView()");
View view = inflater.inflate(R.layout.fragment_email_entry, container, false);
m_hostNameEditText = (EditText) view.findViewById(R.id.hostNameEditText);
// set reference for other fields
applyDefaultsForNewEmailAccount();
return view;
}
private void applyDefaults() {
m_hostNameEditText.setText("");
// set other defaults
}
It may have to do with the fact that my Activity inherits from SingleFragmentActivity, so perhaps it sees that the fragment is already in the view. Still, we know that Fragment.onCreateView() is being called.
public abstract class SingleFragmentActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.single_fragment_activity);
FragmentManager fragmentManager = getFragmentManager();
Fragment fragment = fragmentManager.findFragmentById(R.id.single_frame_container);
if (fragment == null) {
fragment = createFragment();
fragmentManager.beginTransaction()
.add(R.id.single_frame_container, fragment)
.commit();
}
}
public abstract Fragment createFragment();
}
Just like an activity, a fragment will automatically save the data of
any fragment View component in the Bundle by the View component’s id.
And just like in the activity, if you do implement the
onSaveInstanceState( ) method make sure you add calls to the
super.onSaveInstanceState( ) methods so as to retain this automatic
save of View data feature.
source
We all know that when using ViewPager with Fragment and FragmentPagerAdapter we get 3 Fragment loaded: the visible one, and both on each of its sides.
So, if I have 7 Fragments and I'm iterating through them to see which 3 of them are the ones that are loaded, and by that I mean onCreateView() has already been called, how can I determine this?
EDIT: The Fragment doesn't have to be the one that the ViewPager is showing, just that onCreateView() has already been called.
Well logically, this would be a reasonable test if onCreateView has been called:
myFragment.getView() != null;
Assuming you a have a reference to all of the fragments in the pager iterate, them and check if they have a view.
http://developer.android.com/reference/android/app/Fragment.html#getView()
Update
The above answer assumes that your fragments always create a view, and are not viewless fragments. If they are then I suggest sub classing the fragment like so:
public abstract class SubFragment extends Fragment
{
protected boolean onCreateViewCalled = false;
public boolean hasOnCreateViewBeenCalled()
{
return onCreateViewCalled;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup Container, Bundle state){
onCreateViewCalled = true;
return null;
}
}
Just bear in mind that further sub classes will have to call super or set the flag themselves should they override onCreateView as well.
I added an interface to Fragment. Looks like:
protected OnCreateViewCallback createViewCallback = null;
public void setCreateViewCallback(OnCreateViewCallback createViewCallback) {
this.createViewCallback = createViewCallback;
}
public interface OnCreateViewCallback {
void onCreateView();
}
In my onCreateView():
//initialize your view.
if (createViewCallback != null) {
createViewCallback.onCreateView();
createViewCallback = null;
}
return mainView;
From my activity:
if (ocrFragment.getView() == null) {
ocrFragment.setCreateViewCallback(new MainScreenFragment.OnCreateViewCallback() {
#Override
public void onCreateView() {
ocrFragment.ocrImage(picture, false);
}
});
} else {
ocrFragment.ocrImage(picture, false);
}
If you are trying to perform something after onCreateView is called, use onViewCreated:
Called immediately after onCreateView(LayoutInflater, ViewGroup,
Bundle) has returned, but before any saved state has been restored in
to the view. This gives subclasses a chance to initialize themselves
once they know their view hierarchy has been completely created. The
fragment's view hierarchy is not however attached to its parent at
this point.
public void onViewCreated(View view, Bundle savedInstanceState) {
MyActivity myActivity = (MyActivity) getActivity();
MyActivity.newAsyncTask(mPar);
}
You could also check for Fragment.isVisible() because a Fragment is in visible state when it's in the offscreen page limit of a ViewPager.
Edit: But it just really depends on what you really want to achieve with your question. Perhaps some kind of update to all UIs in your Fragments when their UI is ready?
EDIT:
Just another addition, you could listen to onViewCreated() and set a flag. Or notify your Activity and do further work (getActivity() will return your Activity at this point). But really, better state what you want to accomplish with your question.
I have a ViewPager. My FragmentPageAdapter returns the position of the Viewpager in the getItem() methode. But after rotating the Screen the methode returns no value. Why? If I understood it right, everytime you rotate the screen OnCreateView() is called, but why doesn't it return the value any more? Could someone point out how to solve this? Thank you
Edit: My FragmentPageAdapter:
public Fragment getItem(int position) {
return Fragment_results.newInstance(position);
}
My Fragment:
public static Fragment_results newInstance(int i) {
Fragment_results fragment = new Fragment_results();
fragment.mContent = i +"";
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.layout_result, null);
((TextView) view.findViewById(R.id.text)).setText(mContent);
The position is set to 0 upon creating of the ViewPager instance and whenever you set a new adapter. When onCreateView() is called, you're rebuilding the entire app essentially. In order to revert back to the position it was, you must first use onSavedInstanceState(Bundle savedInstanceState) and store the item position via the Bundle.
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("pageItem", myViewPager.getCurrentItem());
}
Then in onCreate, restore the viewPager's state like so:
if (savedInstanceState != null) {
myViewPager.setCurrentItem(savedInstanceState.getInt("pageItem", 0));
}