1

Functionality: I'm developing some sort of configuration wizard, which may have many steps. Each step has it's own configuration, in this case, isSkippable() whether if user can skip the step or not. And a callback method when user taps on menu item to go to next step, or indeed completed the wizard.

Issue: I don't have activity context, and my fields are also null when onStepCompleted is called from activity.

Activity which extends ActionBarActivity and doesn't implement any interface:

private WizardStepListener mStepListener;

private WizardPagerAdapter mPagerAdapter;
private ViewPager mViewPager;

public static Intent getIntent(final Context context) {
    final Intent intent = new Intent(context, WizardActivity.class);
    intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP);
    return intent;
}

@Override
public boolean onOptionsItemSelected(final MenuItem item) {
    if (mStepListener == null) {
        return false;
    }

    switch (item.getItemId()) {
        case R.id.action_step_skip:
            moveToNextStep();

            break;

        case R.id.action_step_done:
        case R.id.action_step_next:

            final boolean stepCompleted = mStepListener.onStepCompleted();
            if (stepCompleted && !isLastStep() || !stepCompleted) {
                    break;
            }

            final Intent intent = LandingActivity.getIntent(this);
            WizardActivity.this.startActivity(intent);
            overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_right);
            WizardActivity.this.finish();

            break;

    }

    return true;
}

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

    final Resources resources = getResources();
    final PagerTabStrip pagerTabStrip = (PagerTabStrip) findViewById(R.id.pager_tab_strip);
    pagerTabStrip.setDrawFullUnderline(false);
    pagerTabStrip.setBackgroundColor(resources.getColor(R.color.tabs_categories_background));
    pagerTabStrip.setTextColor(Color.WHITE);
    pagerTabStrip.setTabIndicatorColor(Color.WHITE);

    mPagerAdapter = new WizardPagerAdapter(getSupportFragmentManager());
    mViewPager = (ViewPager) findViewById(R.id.wizard_pager);
    mViewPager.setAdapter(mPagerAdapter);
    mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {

        @Override
        public void onPageSelected(final int position) {
            setupView(position);
        }

    });

    setupView(0);
}

private void setupView(final int position) {
    final Fragment fragment = mPagerAdapter.getActiveFragment(mViewPager, position);

    mStepListener =
            fragment instanceof WizardStepListener ? (WizardStepListener) fragment : null;

    supportInvalidateOptionsMenu();
}

private boolean isLastStep() { ... }

private boolean moveToNextStep() { ... }

}

Fragment which extends Fragment and implements LoaderManager.LoaderCallbacks>, WizardStepListener:

private CategoriesExpandableAdapter mCatAdapter;
private ViewFlipper mViewFlipper;

/**
 * @return A new instance of fragment WizardCategories.
 */
public static WizardCategories newInstance() {
    return new WizardCategories();
}

@Override
public boolean isSkippable() {
    return false;
}

@Override
public boolean onStepCompleted() {
    if (!isAdded()) {
        return false;
    }

    if (mCatAdapter == null || !mCatAdapter.hasCheckedCategories()) {

        FeedbackToast.getInstance(
                getActivity(),
                "Debes seleccionar al menos una categoría",
                FeedbackToast.NEGATIVE
        ).show();

        return false;
    }

    ...

    return true;
}

The PagerAdapter which extends FragmentPagerAdapter:

private final FragmentManager mFragmentManager;

public WizardPagerAdapter(final FragmentManager fm) {
    super(fm);
    mFragmentManager = fm;
}

@Override
public Fragment getItem(final int position) {
    final Fragment fragment;
    switch (position) {
        case 0:
            fragment = WizardCategories.newInstance();

            break;

        default:
            fragment = new Fragment();

            break;
    }

    return fragment;
}

public Fragment getActiveFragment(final ViewPager container, final int position) {
    final String name = makeFragmentName(container.getId(), position);
    final Fragment fragment = mFragmentManager.findFragmentByTag(name);
    return  fragment != null ? fragment : getItem(position);
}

private static String makeFragmentName(final int viewId, final int index) {
    return "android:switcher:" + viewId + ":" + index;
}

What I've tried:

  • Changing PagerAdapter extends to FragmentStatePagerAdapter with no luck.
  • Retrieving fragment from FragmentManager by using getActiveFragment seen in another maybe related posts.

Additional comments:

  • Fragment's isAdded() always returns false.
  • Calling getActivity() on fragment's onStepCompleted always returns null.
GMuniz
  • 63
  • 7
  • How about turning the table and letting the Wizard fragments hold the MenuItems themselves? Then they could call directly to the activity such as `((YourActivity)getActivity()).moveToNextPage()`. This approach could be done for one general WizardFragment and then let all wizard pages extend from that. – cYrixmorten Apr 29 '15 at 20:32
  • I'm gonna try this approach you're suggesting ... do you think this would solve the fact that I'm losing the activity? – GMuniz Apr 29 '15 at 20:39
  • 1
    Well, the fragment should not be able to show menu items without being attatched to an activity, so I assume yes. – cYrixmorten Apr 29 '15 at 20:40
  • You can call getActivity() in onCreate of your fragment after calling super.onCreate(savedInstanceState). I'm doing it right now in my project and it works fine. – Dave S Apr 29 '15 at 21:19
  • @cYrixmorten your approach was correct! and kinda more elegant! Thanks! wanna post the answer? to flag the question as answered? – GMuniz Apr 30 '15 at 14:00
  • Sounds good, glad to hear that :-) think I did something similar a while back and came to the same conclusion. I'll post it as an answer right away – cYrixmorten Apr 30 '15 at 17:51

2 Answers2

0

You should be able to use getActivity() to get the Activity and store the context in your fragment in onCreate, the main thing to remember is you need to call this after onAttach has been called which links the fragment to the activity.

I currently use the Activity's context in onCreate() in my app without issue.

Alternatively you can override onAttach and get a reference to the Activity directly and store it there:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
}

Reference: Using context in a fragment

Community
  • 1
  • 1
Dave S
  • 3,378
  • 1
  • 20
  • 34
0

How about turning the table and letting the Wizard fragments hold the MenuItems themselves? Then they could call directly to the activity such as ((YourActivity)getActivity()).moveToNextPage().

This approach could be done for one general WizardFragment and then let all wizard pages extend from that.

cYrixmorten
  • 7,110
  • 3
  • 25
  • 33
  • I tidied this even a little more using interfaces, and polishing my existent interfaces. Works like a charm! Thanks! – GMuniz Apr 30 '15 at 18:22