Android: Wizard like Activity with Fragments

androidandroid-activityfragmentwizard

Helo.

I read a lot here and on other sites, but it seems that no one have a working solution for using fragments as wizard like app.

I try to implement one, but got some problems at the moment.

Here is the Activity XML with placeholder for Step-Fragments:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:ads="http://schemas.android.com/apk/lib/com.google.ads"
  android:layout_width="match_parent"
  android:layout_height="match_parent" >

  <FrameLayout
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_above="@+id/cmd_previous"
    android:layout_alignParentTop="true" >
  </FrameLayout>

  <Button
    android:id="@+id/cmd_previous"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"
    android:layout_alignRight="@+id/center_separator_buttons"
    android:text="@string/rpz_cmd_perv" />

  <View
    android:id="@+id/center_separator_buttons"
    style="@style/wd_center_aligner"
    android:layout_alignParentBottom="true" />

  <Button
    android:id="@+id/cmd_next"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignLeft="@+id/center_separator_buttons"
    android:layout_alignParentBottom="true"
    android:layout_alignParentRight="true"
    android:text="@string/rpz_cmd_next" />

</RelativeLayout>

In OnCreate I add the first step to the activity:

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_report_wizard);

  if (this.getIntent().getExtras() != null) {
    Bundle extra = this.getIntent().getExtras();
    if (extra.containsKey(ISelected.ID_KEY)) {
      _ID = extra.getLong(ISelected.ID_KEY);
    }
  }

  if (findViewById(R.id.fragment_container) != null && savedInstanceState == null) {
    _ReportTypeAndName = new ReportWizardTypeAndNameFragment();
    _ReportTypeAndName.setArguments(getIntent().getExtras());
    getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, _ReportTypeAndName).commit();
  }
}

The NEXT-button replace the current Fragment with new one. This seems to work fine.

private void runNext(View view) {
  switch (_CurrentPage) {
    case ReportTypeAndName:
      _ReportSelect = new ReportWizardSelectFragment();
      _ReportSelect.setArguments(getIntent().getExtras());
      getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, _ReportSelect).addToBackStack(null).commit();

      _CurrentPage = ReportWizardPage.ReportSelect;
      break;

    case ReportSelect:
      _CurrentPage = ReportWizardPage.ReportPreview;
      break;

    case ReportPreview:
      _CurrentPage = ReportWizardPage.None;
      break;

    default:
      break;
  }
}

But on the PREVIOUS-Button, this doesn't work.

private void runPrevious(View view) {
  switch (_CurrentPage) {
    case ReportTypeAndName:
      // Umstellung des Typs
      _CurrentPage = ReportWizardPage.None;
      this.finish();
      break;

    case ReportSelect:
      _ReportTypeAndName = new ReportWizardTypeAndNameFragment();
      _ReportTypeAndName.setArguments(getIntent().getExtras());
      getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, _ReportTypeAndName).commit();

      _CurrentPage = ReportWizardPage.ReportTypeAndName;
      break;

    case ReportPreview:
      _CurrentPage = ReportWizardPage.ReportSelect;
      break;

    default:
      break;
  }
}

I got the following Error (LogCat):

FATAL EXCEPTION: main
android.view.InflateException: Binary XML file line #13: Error inflating class fragment
  at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:697)
  at android.view.LayoutInflater.rInflate(LayoutInflater.java:739)
  at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
  at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
  at de.webducer.android.worktime.beta.ui.fragment.ReportWizardTypeAndNameFragment.onCreateView(ReportWizardTypeAndNameFragment.java:40)
  at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:870)
  at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1080)
  at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:622)
  at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1416)
  at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:420)
  at android.os.Handler.handleCallback(Handler.java:605)
  at android.os.Handler.dispatchMessage(Handler.java:92)
  at android.os.Looper.loop(Looper.java:137)
  at android.app.ActivityThread.main(ActivityThread.java:4424)
  at java.lang.reflect.Method.invokeNative(Native Method)
  at java.lang.reflect.Method.invoke(Method.java:511)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
  at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalArgumentException: Binary XML file line #13: Duplicate id 0x7f05008b, tag null, or parent id 0x0 with another fragment for de.webducer.android.worktime.beta.ui.fragment.ReportTypeSelectorSpinnerFragment
  at android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:275)
  at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:669)
  ... 18 more

The line 40 of the Fragment is the inflater of them

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  return inflater.inflate(R.layout.frag_report_wizard_1, container, false);
}

How can I bind (add/remove) the fragments without getting duplicate IDs? Or how can I reuse the fragment without new View-Initialization?

enter image description here

Best Answer

Put your fragments in the backstack and call them back as needed.

Add it to the backstack using addToBackStack:

list = new ListFragment_List();
getFragmentManager().beginTransaction().replace(R.id.pane, listlist).addToBackStack(null).commit();

Pull it back up using popBackStack:

FragmentManager fm = getActivity().getSupportFragmentManager();
fm.popBackStack();

Note this code came from an app of mine using the v4 support library, so the call for the fragment manager will be slightly different if you are developing for Android 3.0+ only.