11

I want to create an Activity which shows a sort of menu a user can go through. By clicking an item, a new screen is shown, allowing the user more options (wizard-like).

I wanted to implement this using Fragments, but it's not working for me.
Right now I have:

main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:id="@+id/main_fragmentcontainer" >

    <fragment
        android:id="@+id/mainmenufragment"
        android:name="com.myapp.MainMenuFragment"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

    <fragment
        android:id="@+id/secondmenufragment"
        android:name="com.myapp.SecondMenuFragment"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

</LinearLayout>

MainMenuFragment with an OnClickListener:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.mainmenu, container, false);

    setupButton(view);
    return view;
}

/* Button setup code omitted */

@Override
public void onClick(View v) {
    SherlockFragment secondRunMenuFragment = (SherlockFragment) getSherlockActivity().getSupportFragmentManager().findFragmentById(R.id.secondmenufragment);
    FragmentTransaction transaction = getSherlockActivity().getSupportFragmentManager().beginTransaction();

    transaction.replace(android.R.id.content, secondMenuFragment); //also crashes with R.id.main_fragmentcontainer
    transaction.addToBackStack(null);
    transaction.commit();
}

Now when I press the button, the application crashes with this logcat:

06-27 01:45:26.309: E/AndroidRuntime(8747): java.lang.IllegalStateException: Can't change container ID of fragment SecondMenuFragment{405e2a70 #1 id=0x7f060029}: was 2131099689 now 2131099687
06-27 01:45:26.309: E/AndroidRuntime(8747): at android.support.v4.app.BackStackRecord.doAddOp(Unknown Source)
06-27 01:45:26.309: E/AndroidRuntime(8747): at android.support.v4.app.BackStackRecord.replace(Unknown Source)
06-27 01:45:26.309: E/AndroidRuntime(8747): at android.support.v4.app.BackStackRecord.replace(Unknown Source)
06-27 01:45:26.309: E/AndroidRuntime(8747): at com.myapp.MainMenuFragment$MyButtonOnClickListener.onClick(MainMenuFragment.java:52)

What am I doing wrong?

nhaarman
  • 98,571
  • 55
  • 246
  • 278
  • [There's a similar question here](http://stackoverflow.com/questions/9092048/swap-two-fragment-simultaneously) – adneal Jun 26 '12 at 23:52
  • If you are trying to create a wizard, what is the second `` for? – CommonsWare Jun 26 '12 at 23:53
  • I've seen those links indeed, but I don't know how to apply them in my case. – nhaarman Jun 26 '12 at 23:53
  • @CommonsWare The first `Fragment` shows the first menu with buttons `1`, `2` and `3`. The second `Fragment` shows a second menu with buttons `1.1`, `1.2` and `1.3`. The plan is to add a third `Fragment` with buttons `2.1`, `2.2` and `2.3`. Or am I approaching this all wrong? – nhaarman Jun 26 '12 at 23:55

3 Answers3

18

I create this main layout:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="horizontal"
   tools:context="com.example.fragmentsexample.MainActivity" >

   <FrameLayout 
      android:id="@+id/contentFragment"
      android:layout_width="fill_parent" 
      android:layout_height="fill_parent" 
      android:layout_weight="1" />

</LinearLayout>

And I replenished in the FrameActivity with:

@Override
public void onCreate(Bundle savedInstanceState) {
  ...
  Fragment fragment = new Dashboard();
  FragmentManager fm = getSupportFragmentManager();
  FragmentTransaction transaction = fm.beginTransaction();
  transaction.replace(R.id.contentFragment, fragment);
  transaction.commit();
  ...
}

And I repleace on onClick Method with the same code, changing Fragment (Dashboard for Events):

@Override
public void onClick.... {
  ...
  Fragment fragment = new Events();
  FragmentManager fm = getSupportFragmentManager();
  FragmentTransaction transaction = fm.beginTransaction();
  transaction.replace(R.id.contentFragment, fragment); //Container -> R.id.contentFragment
  transaction.commit();
  ...
}
jeprubio
  • 17,312
  • 5
  • 45
  • 56
Josep Escobar
  • 445
  • 4
  • 11
16

Personally, I would not have any <fragment> elements.

Step #1: Populate your activity layout with a <FrameLayout> for the variable piece of the wizard, plus your various buttons.

Step #2: In onCreate() of the activity, run a FragmentTransaction to load the first wizard page into the FrameLayout.

Step #3: On the "next" click, run a FragmentTransaction to replace the contents of the FrameLayout with the next page of the wizard.

Step #4: Add in the appropriate smarts for disabling the buttons when they are unusable (e.g., back on the first wizard page).

Also, you will want to think about how the BACK button should work in conjunction with any on-screen "back" button in the wizard. If you want them to both behave identically, you will need to add each transaction to the back stack and pop stuff off the back stack when you handle the "back" button.

Someday, if nobody beats me to it, I'll try to create a wizard-by-way-of-fragments example, or perhaps a reusable component.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • I'm not sure if I understood you right, but I got it working somehow. I've put an empty `` in my activity layout file. Then, in `onCreate()` of the activity, I called `FragmentTransaction.add()` to add `new MainMenuFragment()` to `R.id.main_fragmentcontainer`, the id of the ``. In the `MainMenuFragment` `onClick()` I create a `new SecondMenuFragment()`, and add it the same way, again to `R.id.main_fragmentcontainer`. That at least works. I'm especially confused in step 1 _plus your various buttons_. What do you mean by that? And couldn't I just use `LinearLayout`? – nhaarman Jun 27 '12 at 00:13
  • @Niek: "What do you mean by that?" -- usually a wizard interface has "previous" (or "back") and "next" buttons. There is no reason to put those in the fragments, as they stay the same throughout all wizard steps, so I would presume you would put them directly in the activity. "And couldn't I just use LinearLayout?" -- for what? – CommonsWare Jun 27 '12 at 10:26
  • Oh those buttons, I thought you meant my menu buttons. And you suggested a FrameLayout in step 1, but I guess that has to do with the overlay of the "next" and "previous" buttons. Thanks for your help! – nhaarman Jun 27 '12 at 10:56
  • @Niek: The `FrameLayout` was the "slot" in which to put the fragments. `FrameLayout` is often used for this, as it's a pretty cheap container processing-wise, I suppose. So long as it behaves as you wish, any `ViewGroup` should technically work. – CommonsWare Jun 27 '12 at 11:04
0

Another option is to use the Android-WizardPager by Roman Nurik.

Key features:

  • Branching, or the ability for wizard steps to influence the availability of later steps
  • Allowing the user to review before committing
  • Allowing the user freeform navigation between wizard steps
  • Support for required and optional steps
  • Support for step classes (technically, each step is an instance of a Java class, so you can have multiple instances within the wizard)

More info here.

LordRaydenMK
  • 13,074
  • 5
  • 50
  • 56