4

I am using a FragmentPagerAdapter to create a multi-pages UI with different fragments. What I want to do is to find the Fragment using the position of the Fragment.

I have applied the method of finding the nametag of fragments and use .FindFragmentbyTag() but the .FindFragmentbyTag always return me null value. The structure seems to be correct so I am not sure why I get null values, please look through and help is appreciated.

Here is the code

MainActivity.java
public void onCreate(Bundle savedInstanceState) {
    this.getSupportFragmentManager();
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    MainActivity.instance=this;
    //Initialize pager
    vpPager = (ViewPager)findViewById(R.id.mypager);
    pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
    vpPager.setAdapter(pagerAdapter);
    this.mapFragment = (MapFragment)this.findFragmentByPosition(0);
    this.drFragment = (DRFragment)this.findFragmentByPosition(1);
    this.sensorFragment = (SensorFragment)this.findFragmentByPosition(2);
    ......
}

protected Fragment  findFragmentByPosition(int position) {
    int pagerid = this.vpPager.getId();
    long pagerAdapterid = this.pagerAdapter.getItemId(position);
    return getSupportFragmentManager().findFragmentByTag("android:switcher:" + pagerid + ":" + pagerAdapterid + "");
}

MyPagerAdapter.java
import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.view.View;

public class MyPagerAdapter extends FragmentPagerAdapter {
    private static final String TAG = "TM_PageAdapter";
    private static int numItems = 3;

    public MyPagerAdapter(FragmentManager fragmentManager){
        super(fragmentManager);
    }

    public int getCount() {
        return numItems;
    }

    @Override
    public Fragment getItem(int position) {
        switch(position){
            case 0:
                return MapFragment.newInstance(0, "Map Information");
            case 1:
                return DRFragment.newInstance(1, "DR Information");
            case 2: 
                return SensorFragment.newInstance(2, "Sensor Information");
            default: 
                return null;
        }
    }

    @Override
    public CharSequence getPageTitle(int position) {
        switch(position){
            case 0:
                return "Map Information";
            case 1:
                return "DR Information";
            case 2: 
                return "Sensor Information";
            default:
                return null;
        }
    }

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


    @Override
    public boolean isViewFromObject(View arg0, Object arg1) {
        return arg0 == ((View) arg1);

    }

    @Override
    public Parcelable saveState() {
        return null;
    }

Here is one of the code of the .newInstance:

public static MapFragment newInstance(int position, String title){
    MapFragment mapFragment = new MapFragment();
    Bundle args = new Bundle();
    args.putInt("current_page", 0);
    args.putString("page_tile", "Map Information");
    mapFragment.setArguments(args);
    return mapFragment;
}
HpTerm
  • 8,151
  • 12
  • 51
  • 67
Thomas Dang
  • 201
  • 3
  • 4
  • 14
  • 2
    I don't see you calling makeFragmentName(...) anywhere. Can you provide some code like the contents of MapFragment.newInstance(...)? – Daniel Zolnai Jul 17 '14 at 08:23
  • I added the code part for .newInstnce, This is my first time working with FragmentPagerAdapter so I mostly follow the tutorial online. I might miss some important part (I do suspect so). Please show me where I miss, thank you :) – Thomas Dang Jul 17 '14 at 08:28
  • 1
    From what I see, you don't call the makeFragmentName(), so the fragments won't have any tags. That's a reason why findFragmentByTag() won't find them. Also, you don't have to add the viewId to the tag. Just the index should be enough. – Daniel Zolnai Jul 17 '14 at 08:29
  • 2
    Here is what I got from the other post: http://stackoverflow.com/questions/11976397/android-getting-fragment-that-is-in-fragmentpageradapter The Fragments supplied by the FragmentPagerAdapter are auto-tagged when they're instantiated. You can retrieve the tag with this method: .... So isn't the function findFragmentbyPosition(position) in the MainActivity do the trick? I think I applied both of the answer on that page and forgot to delete one out – Thomas Dang Jul 17 '14 at 08:35
  • @DanielZolnai and ThomasDang, seems the problem was elsewhere, let's get rid of all thoses useless comments. – HpTerm Jul 17 '14 at 08:44

3 Answers3

3

In findFragmentByPosition, you are not using the index, when looking for the fragment:

getSupportFragmentManager().findFragmentByTag("android:switcher:" + pagerid + ":" + pagerAdapterid + "");

The second one should be the index, not the pagerAdapterId. So the correct code would be:

getSupportFragmentManager().findFragmentByTag("android:switcher:" + pagerid + ":" + position);

There's also no need to concatenate the last empty string.

Daniel Zolnai
  • 16,487
  • 7
  • 59
  • 71
  • The function: long pagerAdapterid = this.pagerAdapter.getItemId(position); would give out the exact same thing as position (I used debug mode and find out) so it is not problem. I also double check by change the code according to your answer. still null pointer @@. But thanks for the help – Thomas Dang Jul 17 '14 at 08:49
  • You are just adding an extra step then :) It is also possible, that you fragment just doesn't exist, because it is not visible, and has been destroyed. You can increase the amount of fragments being stored with ViewPager.setOffscreenPageLimit(int pages); – Daniel Zolnai Jul 17 '14 at 08:51
  • I am not sure how the fragment are created though, when I try to put a BreakPoint at the getItem(position) in the MyPagerAdapter it did not go through after the vpPager.setAdapter(pagerAdapter); The tutorial I followed is: https://github.com/thecodepath/android_guides/wiki/ViewPager-with-FragmentPagerAdapter And it seems that I have done what is needed @@. – Thomas Dang Jul 17 '14 at 08:58
  • 2
    It is also possible, that the ViewPager has not created the fragments yet, because you are referencing them as soon as you set the adapter. SensorFragment will surely be null, because the pager loads the first fragment, and one extra in the background. – Daniel Zolnai Jul 17 '14 at 09:02
  • Will the Pager load all of the fragments I have, since I am referencing all of the fragments. And is there a way for the main activity to wait for the loading pages to complete then continue on? Thanks for your help :) – Thomas Dang Jul 17 '14 at 09:40
  • 2
    You are not referencing them, you are trying to get a reference to them. You could use a ViewPager.OnPageChangeListener and ask for the reference in onPageSelected(int position). – Daniel Zolnai Jul 17 '14 at 09:45
  • My program only have 3 tabs and I intends to load them all in one go with vpPager.setOffScreenLimit(2); And since I loads all of the fragments I want to get a reference to all of them. Get onPageSelected will only give me the reference for the active page. This may seem stupid but is there a way to holds front to holds till you load all the pages in? – Thomas Dang Jul 17 '14 at 10:03
1

I used this approach in the past and it worked quite well.

In your adapter you can declare a instance variable as follow:

private Map<Integer, Fragment> mPageReferenceMap = new HashMap<Integer, Fragment>();

In your getItem() put a reference of the fragment in the map

mPageReferenceMap.put(position, f);

Override the InstantiateItem method:

/**
 * After an orientation change, the fragments are saved in the adapter, and
 * I don't want to double save them: I will retrieve them and put them in my
 * list again here.
 */
@Override
public Object instantiateItem(ViewGroup container, int position) {
   Fragment fragment = (Fragment) super.instantiateItem(container, position);
   mPageReferenceMap.put(position, fragment);
   return fragment;
}

Override the onDestroyItem to clean up references:

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    super.destroyItem(container, position, object);
    mPageReferenceMap.remove(position);
}

And finally get the fragment depending on its position:

public Fragment getFragment(int key) {
    return mPageReferenceMap.get(key);
}

Hope it helps.

AlexBalo
  • 1,258
  • 1
  • 11
  • 16
1

It seems like the thing that caused my reference to go to null is because Loading fragments is done in background and when I reference those fragments the loading is not yet complete (Thank you Daniel Zolnai for poiting that out, I would give you right answer tick if u put it as a answer) My thoughts for solution is now:

-Load all the pages you have if the number of pages are small using (ViewPager).setOffScreenLimit(int limit)

-Pause the main activity and wait till all pages are loaded then continue on.

Now I am stuck at the second part, if anyone know how to wait or delay the MainActivty for loading pages to complete or force the loading task to foreground, it would be a big help to me, thanks

Thomas Dang
  • 201
  • 3
  • 4
  • 14