Skip to content
This repository has been archived by the owner on Apr 19, 2018. It is now read-only.

fixes NPE ActionBar.TabListener #402

Merged
merged 2 commits into from
Mar 24, 2012
Merged

fixes NPE ActionBar.TabListener #402

merged 2 commits into from
Mar 24, 2012

Conversation

gabrielittner
Copy link
Contributor

This is a fix for Issue #391. The explanation is in the commit message.

For testing you can overwrite the FragmentTabs class in the fragment sample with the following code:

public class FragmentTabs extends SherlockFragmentActivity {

    ActionBar actionBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setTheme(SampleList.THEME); //Used for theme switching in samples
        super.onCreate(savedInstanceState);

        actionBar = getSupportActionBar();
        actionBar.addTab(actionBar.newTab().setText("Simple")
            .setTabListener(new TabListener<FragmentStackSupport.CountingFragment>
            (this, "simple", FragmentStackSupport.CountingFragment.class)));
        actionBar.addTab(actionBar.newTab().setText("Contacts")
            .setTabListener(new TabListener<CursorLoaderListFragment>
            (this, "contacts", LoaderCursorSupport.CursorLoaderListFragment.class)));
        actionBar.addTab(actionBar.newTab().setText("Custom")
            .setTabListener(new TabListener<AppListFragment>
            (this, "custom", LoaderCustomSupport.AppListFragment.class)));
        actionBar.addTab(actionBar.newTab().setText("Throttle")
            .setTabListener(new TabListener<ThrottledLoaderListFragment>
            (this, "throttle", LoaderThrottleSupport.ThrottledLoaderListFragment.class)));
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    }

public static class TabListener<T extends Fragment> implements ActionBar.TabListener {
    private Fragment mFragment;
    private final Activity mActivity;
    private final String mTag;
    private final Class<T> mClass;

    public TabListener(Activity activity, String tag, Class<T> clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
    }

    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        if (ft == null) {
             Log.i("tabselected", "fragmenttransaction == null");
             return;
        }
        if (mFragment == null) {
            mFragment = Fragment.instantiate(mActivity, mClass.getName());
            ft.add(android.R.id.content, mFragment, mTag);
        } else {
            ft.attach(mFragment);
        }
    }

    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        if (ft == null) {
            Log.i("tabunselected", "fragmenttransaction == null");
                return;
        }
        if (mFragment != null) {
            ft.detach(mFragment);
        }
    }

    public void onTabReselected(Tab tab, FragmentTransaction ft) {
    }
}
}

The implementation of onTabSelected() uses the instance variable 
mFragmentTransaction, which is hand over to the listeners 
onTabSelected(). At the start of an Activity, which uses a TabListener
a NullPointerException is thrown, because the FragmentTransaction is
null. The only point where a FragmentTransaction is assigned to 
mFragmentTransaction is onTabUnselected().

I guess the intended behavior is that the transaction created in
onTabUnselected() is used in onTabSelected() afterwards and the
detaching/attaching is done in the same transaction (because
onTabUnselected() doesn't call commit on the transaction itself). 

However at the start just onTabSelected() is called and not
onTabUnselected(), because there was nothing selected before. So
mFragmentTransaction is null and the Exception is thrown. I added an
if-check to  receive the FragmentTransaction the same way
onTabUnselected() does, when mFragmentTransaction is null.
@JakeWharton
Copy link
Owner

Can you add an instanceof check to the if statement as well? If someone is using tabs inside of a regular SherlockActivity you'll get a ClassCastException on the activity start.

JakeWharton added a commit that referenced this pull request Mar 24, 2012
@JakeWharton JakeWharton merged commit a1c502f into JakeWharton:dev Mar 24, 2012
@SimoneCasagranda
Copy link

Hi to all :)
I don't really like this solution because if you rotate your device the onTabUnselected isn't called and after rotation you'll find two "lastVisibleFragment".

I have solved using directly the FragmentManager. For example for onTabSelected:

public void onTabSelected(Tab tab, FragmentTransaction ft) {
if (ft == null) {
Log.i(TAG, "FragmentTransaction == null during Tab selection");
return;
}
Fragment fragment = mFragmentManager.findFragmentByTag(mTag);
if(fragment == null) {
ft.add(android.R.id.content, Fragment.instantiate(mActivity, mClass.getName()), mTag);
} else {
ft.attach(fragment);
}
}

Is this a valid solution?

Thanks a lot (and thanks Jake for your great work!),
Simone

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants