Je développe une application utilisant Android 4.0 ICS et des fragments.
Considérez cet exemple modifié de l'exemple d'application de démonstration de l'API ICS 4.0.3 (niveau d'API 15):
public class FragmentTabs extends Activity {
private static final String TAG = FragmentTabs.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ActionBar bar = getActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
bar.addTab(bar.newTab()
.setText("Simple")
.setTabListener(new TabListener<SimpleFragment>(
this, "mysimple", SimpleFragment.class)));
if (savedInstanceState != null) {
bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
Log.d(TAG, "FragmentTabs.onCreate tab: " + savedInstanceState.getInt("tab"));
Log.d(TAG, "FragmentTabs.onCreate number: " + savedInstanceState.getInt("number"));
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
}
public static class TabListener<T extends Fragment> implements ActionBar.TabListener {
private final Activity mActivity;
private final String mTag;
private final Class<T> mClass;
private final Bundle mArgs;
private Fragment mFragment;
public TabListener(Activity activity, String tag, Class<T> clz) {
this(activity, tag, clz, null);
}
public TabListener(Activity activity, String tag, Class<T> clz, Bundle args) {
mActivity = activity;
mTag = tag;
mClass = clz;
mArgs = args;
// Check to see if we already have a fragment for this tab, probably
// from a previously saved state. If so, deactivate it, because our
// initial state is that a tab isn't shown.
mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);
if (mFragment != null && !mFragment.isDetached()) {
Log.d(TAG, "constructor: detaching fragment " + mTag);
FragmentTransaction ft = mActivity.getFragmentManager().beginTransaction();
ft.detach(mFragment);
ft.commit();
}
}
public void onTabSelected(Tab tab, FragmentTransaction ft) {
if (mFragment == null) {
mFragment = Fragment.instantiate(mActivity, mClass.getName(), mArgs);
Log.d(TAG, "onTabSelected adding fragment " + mTag);
ft.add(android.R.id.content, mFragment, mTag);
} else {
Log.d(TAG, "onTabSelected attaching fragment " + mTag);
ft.attach(mFragment);
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
if (mFragment != null) {
Log.d(TAG, "onTabUnselected detaching fragment " + mTag);
ft.detach(mFragment);
}
}
public void onTabReselected(Tab tab, FragmentTransaction ft) {
Toast.makeText(mActivity, "Reselected!", Toast.LENGTH_SHORT).show();
}
}
public static class SimpleFragment extends Fragment {
TextView textView;
int mNum;
/**
* When creating, retrieve this instance's number from its arguments.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(FragmentTabs.TAG, "onCreate " + (savedInstanceState != null ? ("state " + savedInstanceState.getInt("number")) : "no state"));
if(savedInstanceState != null) {
mNum = savedInstanceState.getInt("number");
} else {
mNum = 25;
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
Log.d(TAG, "onActivityCreated");
if(savedInstanceState != null) {
Log.d(TAG, "saved variable number: " + savedInstanceState.getInt("number"));
}
super.onActivityCreated(savedInstanceState);
}
@Override
public void onSaveInstanceState(Bundle outState) {
Log.d(TAG, "onSaveInstanceState saving: " + mNum);
outState.putInt("number", mNum);
super.onSaveInstanceState(outState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(FragmentTabs.TAG, "onCreateView " + (savedInstanceState != null ? ("state: " + savedInstanceState.getInt("number")) : "no state"));
textView = new TextView(getActivity());
textView.setText("Hello world: " + mNum);
textView.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb));
return textView;
}
}
}
Voici la sortie extraite de l'exécution de cet exemple, puis de la rotation du téléphone:
06-11 11:31:42.559: D/FragmentTabs(10726): onTabSelected adding fragment mysimple
06-11 11:31:42.559: D/FragmentTabs(10726): onCreate no state
06-11 11:31:42.559: D/FragmentTabs(10726): onCreateView no state
06-11 11:31:42.567: D/FragmentTabs(10726): onActivityCreated
06-11 11:31:45.286: D/FragmentTabs(10726): onSaveInstanceState saving: 25
06-11 11:31:45.325: D/FragmentTabs(10726): onCreate state 25
06-11 11:31:45.340: D/FragmentTabs(10726): constructor: detaching fragment mysimple
06-11 11:31:45.340: D/FragmentTabs(10726): onTabSelected attaching fragment mysimple
06-11 11:31:45.348: D/FragmentTabs(10726): FragmentTabs.onCreate tab: 0
06-11 11:31:45.348: D/FragmentTabs(10726): FragmentTabs.onCreate number: 0
06-11 11:31:45.348: D/FragmentTabs(10726): onCreateView state: 25
06-11 11:31:45.348: D/FragmentTabs(10726): onActivityCreated
06-11 11:31:45.348: D/FragmentTabs(10726): saved variable number: 25
06-11 11:31:45.348: D/FragmentTabs(10726): onCreateView no state
06-11 11:31:45.348: D/FragmentTabs(10726): onActivityCreated
Ma question est la suivante: pourquoi onCreateView et onActivityCreated sont-ils appelés deux fois? La première fois avec un Bundle avec l'état enregistré et la deuxième fois avec un saveInstanceState nul?
Cela pose des problèmes de conservation de l'état du fragment lors de la rotation.