knucKles
knucKles

Reputation: 1

Android Activity communicating with Tab: Tab is null

For my latest Android application (API level 15) I used the new architecture where each Tab in the GUI is represented by a Fragment class which is registered at a PagerAdapter and Tab events are handled by a TabListener.

However, when I want to send data between two tabs I run into a problem. I used the code samples from this page and wrote the following code to send data from a Tab to my Activity:

@Override
public void onDataReceived(Map<String, List<Map<Integer, String>>> receivedData) {

    Log.i("CarerActivity:onDataReceived", "Map received by Fragment.");
    Log.i("CarerActivity:onDataReceived", receivedData.toString());
    Log.i("Fragment ID", ""+R.id.configurationfragment);

    TabConfiguration configurationFragment = (TabConfiguration) getSupportFragmentManager().findFragmentById(R.id.configurationfragment);

    if(configurationFragment != null){
        Log.i("CarerActivity:onDataReceived", "Fragment not null.");
    }else{
        Log.i("CarerActivity:onDataReceived", "Fragment null!");
    }
}

The receivedData.toString() is displayed correctly, so the Tab sends its data to the Activity. A Fragment ID is also displayed. After that the output is always "CarerActivity:onDataReceived Fragment null!" which makes it impossible for me to call any methods of the Fragment (because it is null).

In my R.java the line

public static final int configurationfragment=0x7f080001;

can be found under

    public static final class id{

.

Anyone got an idea what I am doing wrong or where to look for the error?

Setup: Samsung Galaxy Nexus with Android 4.0.3, API level 15, Java 1.6, Mac OS 10.8, Eclipse Juno.

Upvotes: 0

Views: 494

Answers (1)

florianmski
florianmski

Reputation: 5643

Here is an interesting article about FragmentPagerAdapter

FragmentPagerAdapter will detach each fragment as you swipe through the list, but keep them in memory so they can simply be reattached when the user swipes back. If you have a larger number of Fragments, the FragmentStatePagerAdapter is worth considering as it will remove them, with the downside being they need to be rebuilt as the user swipes back to them. So, if you have fewer, more complex fragments the FragmentPagerAdapter makes sense, but consider FragmentStatePagerAdapter for larger sets.

Basically when you swipe from one fragment to another, the previous fragment is destroyed (= null).

One way to overcome this issue will be to hold the new data in a third party class or in your activity. Then when the fragment is recreated pass it a bundle with the new data.

Here is an exemple :

public class PagerUserAdapter extends FragmentStatePagerAdapter
{
    private final static String titles[] = {"Profile", "Badges", "Skills", "Accomplishments"};

    private Bundle args;

    public PagerUserAdapter(FragmentManager fm, CWUser u) 
    {
        super(fm);

        this.args = new Bundle();
        args.putSerializable(Constants.BUNDLE_USER, u);
    }

    public void refresh(CWUser u)
    {
        args.putSerializable(Constants.BUNDLE_USER, u);
        this.notifyDataSetChanged();
    }

    @Override
    public CharSequence getPageTitle(int position)
    {
        return titles[position];
    }

    @Override
    public Fragment getItem(int position) 
    {
        switch(position)
        {
        case 0:
            return ProfileFragment.newInstance(args);
        case 1:
            return BadgesFragment.newInstance(args);
        case 2:
            return SkillsFragment.newInstance(args);
        case 3:
            return AccomplishmentsFragment.newInstance(args);
        default:
            return ProfileFragment.newInstance(args);
        }
    }

    @Override
    public int getCount() 
    {
        return titles.length;
    }

    @Override
    public int getItemPosition(Object object) 
    {
        return POSITION_NONE;
    }
}

public class BadgesFragment extends BaseFragment
{    
    public static BadgesFragment newInstance(Bundle args)
    {
        BadgesFragment f = new BadgesFragment();
        f.setArguments(args);
        return f;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) 
    {
        super.onActivityCreated(savedInstanceState);

        CWUser u = (CWUser) getArguments().get(Constants.BUNDLE_USER);
            //do what you want with your new data   
    }
}

In your activity when you get new data, all you have to do is call the refresh method of your adapter with the new data. This is how I do it, it may have way more elegant solution, I'm open to any suggestion. The exemple is based on one of my open source app, here is the project with full code if you want to dig.

Upvotes: 1

Related Questions