Search code examples
androidandroid-fragmentsandroid-viewpagerfragment-lifecycle

ViewPager doesn't show first fragment, if its contents are loaded dynamically


I am facing a very strange issue, while building a viewPager with fragments in android application. I have an activity, that builds the layout and two fragments: each of them contains RecyclerView and loads its contents dynamically from the server. When I've tested it with static content of the Fragments everything works just great, but as I switch to pre-tested and working dynamic-content Fragments, the ViewPager never loads the first one, only the second. If I swap them, the viewPager still loads only the second one. In the first Tab I always see only a white blank field. Any help will be appreciated.

The activity part:

    tabLayout = (TabLayout) findViewById(R.id.contacts_tabs_layout);
    adapter = new ContactsPagerAdapter(getSupportFragmentManager());

    viewPager.setAdapter(adapter);
    tabLayout.setupWithViewPager(viewPager);

The activity layout :

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar_parent"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize" />

    <android.support.design.widget.TabLayout
        android:id="@+id/contacts_tabs_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabMaxWidth="0dp"
        app:tabGravity="fill"
        app:tabMode="scrollable">

    </android.support.design.widget.TabLayout>
</android.support.design.widget.AppBarLayout>

<android.support.v4.view.ViewPager
    android:id="@+id/view_pager_contacts_list"
    android:layout_width="match_parent"
    android:layout_height="fill_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" />

The fragment layout:

<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/contacts_list" android:layout_width="fill_parent" android:layout_height="match_parent" />

The fragment's relevant code:

 contactsList = (RecyclerView) v.findViewById(R.id.contacts_list);
    contactsList.setLayoutManager(new LinearLayoutManager(getActivity()));
    adapter = new ContactsListAdapter(getActivity());
    contactsList.setAdapter(adapter);


    controller.getContacts(new CommunicationListener() {
        @Override
        public void onFileReceived(FilePath filePath, byte[] data) {
            System.out.println("RECEIVED CONTACTS DATA FILE WITH SIZE OF " + data.length);
            final Contact c = Utils.deserializeContact(data);

            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    adapter.add(c);
                }
            });
        }
    });
    return v;

The adapter's part:

 public ContactsPagerAdapter(FragmentManager fragmentManager) {
    super(fragmentManager);

}


@Override
public Fragment getItem(int position)
{
    switch(position){
        case 0:
            return new ContactsListFragment();
        case 1:
            return new TestFragment();
        default:
            return null;
    }
}

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

@Override
public int getCount() {
    return 2;
}

Solution

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

    From the javadoc of PagerAdapter POSITION_NONE means the item is no longer present.

    /** * Called when the host view is attempting to determine if an item's position * has changed. Returns {@link #POSITION_UNCHANGED} if the position of the given * item has not changed or {@link #POSITION_NONE} if the item is no longer present * in the adapter. * *

    The default implementation assumes that items will never * change position and always returns {@link #POSITION_UNCHANGED}. * * @param object Object representing an item, previously returned by a call to * {@link #instantiateItem(View, int)}. * @return object's new position index from [0, {@link #getCount()}), * {@link #POSITION_UNCHANGED} if the object's position has not changed, * or {@link #POSITION_NONE} if the item is no longer present. */

    You should return the position of the item or POSITION_UNCHANGED.

    Also:

    `PagerAdapter` supports data set changes. Data set changes must occur on the * `main thread` and must end with a call to {@link #notifyDataSetChanged()} similar * to AdapterView adapters derived from {@link android.widget.BaseAdapter}. A data * set change may involve pages being added, removed, or changing position. The * ViewPager will keep the current page active provided the adapter implements * the method {@link #getItemPosition(Object)}.

    You need to call notifyDatasetChanged() after adding a fragment.