General context:
I have a MainActivity with a MainFragment. MainFragment has a ViewPager and I use a FragmentStatePagerAdapter to display DetailFragments based on the content of a SqliteDatabase. The content of this database is visible and managable in LocationActivity. When I click on an item in LocationActivity I use intent.putextra to attach the position of the item and then display the correct position in the ViewPager in MainFragment. When I add an item in LocationActivity it saves the position of the item in SharedPreferences and I get the correct position in MainFragment to display the fragment of the item I just added.
Problem:
It works but when a configuration change occurs I lose the actual page selected of the ViewPager. So I save the position with onSaveInstanceState(), if it's not null I retrieve it in onCreateView() and in onViewCreated() I go through the if statements to get the position required for ViewPager.setCurrentItem() depending on the condition. But then onCreate() is called a second time, onSaveInstanceState() is then null and I lose the page that was selected before the configuration change. Do you know why is it called twice? What can I do to prevent that? Thanks for your help
Log
06-24 09:15:05.974 activityMain.MainFragment: onLoadFinished() onPageSelected() 2
06-24 09:15:08.276 activityMain.MainFragment: onSaveInstanceState() 2
06-24 09:15:08.320 activityMain.MainFragment: onCreate()
06-24 09:15:08.342 activityMain.MainFragment: onCreateView()
06-24 09:15:08.342 activityMain.MainFragment: onCreateView() savedInstanceState Position: 2
06-24 09:15:08.349 activityMain.MainFragment: onViewCreated() get position from mCurrentPositionState 2
06-24 09:15:08.349 activityMain.MainFragment: onActivityCreated()
06-24 09:15:08.394 activityMain.MainFragment: onCreate()
06-24 09:15:08.396 activityMain.MainFragment: onCreateView()
06-24 09:15:08.401 activityMain.MainFragment: onViewCreated()) get position from SharedPreferences 4
06-24 09:15:08.401 activityMain.MainFragment: onActivityCreated()
06-24 09:15:08.407 activityMain.MainFragment: onResume()
06-24 09:15:08.458 activityMain.MainFragment: restartCursorLoader
06-24 09:15:08.508 activityMain.MainFragment: onLoadFinished()
06-24 09:15:08.537 activityMain.MainFragment: onLoadFinished() setCurrentItem: 4
A minimal, complete, and verifiable example
MainFragment.java
public class MainFragment extends Fragment {
public MainFragment() {
// Required empty public constructor
}
private static final String TAG = MainFragment.class.getName();
private static final String PAGE_SELECTED = "state_instance";
private MainPagerAdapter mAdapter;
private ViewPager mViewPager;
private int mLocationPosition;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (savedInstanceState != null) {
mLocationPosition = savedInstanceState.getInt(PAGE_SELECTED);
Log.i(TAG, "onCreateView() savedInstanceState not null, position: " + mLocationPosition);
} else {
mLocationPosition = 0;
Log.i(TAG, "onCreateView() savedInstanceState null, position: " + mLocationPosition);
}
return inflater.inflate(R.layout.fragment_main, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mViewPager = (ViewPager) view.findViewById(R.id.view_pager);
mAdapter = new MainPagerAdapter(getActivity().getSupportFragmentManager());
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mViewPager.setAdapter(mAdapter);
mViewPager.setCurrentItem(mLocationPosition, false);
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
mLocationPosition = position;
Log.i(TAG, "onActivityCreated() mLocationPosition value: " + mLocationPosition);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(PAGE_SELECTED, mLocationPosition);
Log.i(TAG, "onSaveInstanceState() mLocationPosition value: " + mLocationPosition);
super.onSaveInstanceState(outState);
}
}
MainActivity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, new MainFragment());
fragmentTransaction.commit();
}
}
MainDetailFragment
public class MainDetailFragment extends Fragment {
private static final String TAG = MainDetailFragment.class.getName();
private TextView mTextViewLocation;
protected static final String ARGUMENT_PAGE = "location_page";
protected static final String ARGUMENT_NAME = "location_name";
private String mLocation;
public MainDetailFragment() {
}
protected static MainDetailFragment newInstance(int page, String locationName) {
MainDetailFragment mainDetailFragment = new MainDetailFragment();
Bundle arguments = new Bundle();
arguments.putInt(MainDetailFragment.ARGUMENT_PAGE, page);
arguments.putString(MainDetailFragment.ARGUMENT_NAME, locationName);
mainDetailFragment.setArguments(arguments);
return mainDetailFragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments().containsKey(ARGUMENT_NAME)) {
mLocation = getArguments().getString(ARGUMENT_NAME);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_main_detail, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mTextViewLocation = (TextView) view.findViewById(R.id.tv_city_name);
mTextViewLocation.setText(mLocation);
}
}
MainPagerAdapter
public class MainPagerAdapter extends FragmentStatePagerAdapter {
private static final String TAG = MainPagerAdapter.class.getName();
private static int NUM_ITEMS = 3;
public MainPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
// Returns total number of pages
@Override
public int getCount() {
return NUM_ITEMS;
}
// Returns the fragment to display for that page
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return MainDetailFragment.newInstance(0, "Paris");
case 1:
return MainDetailFragment.newInstance(1, "London");
case 2:
return MainDetailFragment.newInstance(2, "New York");
default:
return null;
}
}
}
You need to make sure you're not adding your MainFragment a second time after the configuration change. You should update your Activity to look like this:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
if (savedInstanceState != null) {
return;
}
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, new MainFragment());
fragmentTransaction.commit();
}
The reason for this is that the Activity
itself already has a record of the MainFragment
being added and will automatically restore it after a configuration change. If you perform the same transaction again, first you'll see the restored MainFragment
starting up, then its going to get replaced by the new one from the new Fragment
transaction and a new, different MainFragment
is going to go through its own initialization process. That results in what appears to be multiple calls to onCreate
for MainFragment
.