Rozwijam aplikację przy użyciu systemu Android 4.0 ICS i fragmentów.
Rozważmy ten zmodyfikowany przykład z przykładowej aplikacji demonstracyjnej interfejsu API ICS 4.0.3 (poziom 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;
}
}
}
Oto dane wyjściowe pobrane po uruchomieniu tego przykładu, a następnie obróceniu telefonu:
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
Moje pytanie brzmi: dlaczego onCreateView i onActivityCreated są wywoływane dwukrotnie? Za pierwszym razem z pakietem z zapisanym stanem, a za drugim z zerowym zapisanym stanem instancji?
Powoduje to problemy z zachowaniem stanu fragmentu podczas obrotu.