Subby
Subby

Reputation: 5480

Cannot see Fragments in ViewPager

Although I can scroll in the ViewPager, the physical fragments themselves are not visible for some reason.

FYI - I have an Activity with tabs using a ViewPager. Inside the first tab/fragment, I have the code below. So essentially, I have an App with a ViewPager controlling the tabs and there's another ViewPager inside one of the Tabs which is to control a bunch of images. I have tested the Fragment in question by putting it inside the top level view pager and it works perfectly fine! It's only when I put it inside the view pager in question does it not render anything...

So, this is the hierarchy for a better understanding:

MainActivity has a ...
  ViewPager (3 fragments showing the tab content) has a ...
    1st TabFragment has a ...
      ViewPager (3 fragments showing images) <--- I can't see these, but I can swipe on them for some reason.

Unfortunately... None of the Fragments are visible, HOWEVER... I can still swipe for some reason to the last fragment...Just nothing is visibly shown...

Layout with the ViewPager inside:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical">

    <android.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

Code with ViewPager

final List<Fragment> imageFragments = new ArrayList<>();

for (final UserImage userImage : user.getImages()) {
    final SizeImage processed = userImage.getProcessed();
    imageFragments.add(UserImageFragment.newInstance(processed.getFullsize()));
}

final ImagesPagerAdapter imagesAdapter = new ImagesPagerAdapter(getFragmentManager(), imageFragments);
viewPager.setAdapter(imagesAdapter);

The PagerAdapter:

public class ImagesPagerAdapter extends FragmentStatePagerAdapter {

@NonNull
private List<Fragment> fragments;

public ImagesPagerAdapter(@NonNull final FragmentManager fragmentManager,
                          @NonNull final List<Fragment> fragments) {
    super(fragmentManager);
    this.fragments = fragments;
}

@Override
public Fragment getItem(final int position) {
    return fragments.get(position);
}

@Override
public int getCount() {
    return fragments.size();
    }
}

The Fragment:

public class UserImageFragment extends Fragment {

@BindView(R.id.image_view)
ImageView imageView;

public static UserImageFragment newInstance(final String url) {
    final UserImageFragment fragment = new UserImageFragment();

    Bundle args = new Bundle();
    args.putString("url", url);
    fragment.setArguments(args);

    return fragment;
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    final View view = inflater.inflate(R.layout.fragment_user_image, container, false);

    ButterKnife.bind(this, view);

    final Bundle arguments = getArguments();

    if (arguments != null) {
        final String url = arguments.getString("url", null);
        if (url != null) {
            final Uri uri = Uri.parse(url);

            Picasso.with(getActivity()).load(url).into(imageView, new Callback() {
                @Override
                public void onSuccess() {
                    Toast.makeText(getActivity(), "Success", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onError() {
                    Toast.makeText(getActivity(), "Fail", Toast.LENGTH_SHORT).show();
                }
            });
          }
        }
      return view;
     }
   }

And here is xml code:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/md_red_900">

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:background="@color/md_yellow_500" />

</RelativeLayout>

Upvotes: 0

Views: 1294

Answers (2)

Ameer
Ameer

Reputation: 2769

To show Fragments inside another Fragment, use getChildFragmentManager() instead of getFragmentManager()

final ImagesPagerAdapter imagesAdapter = new 
 ImagesPagerAdapter(getChildFragmentManager(), imageFragments);
   viewPager.setAdapter(imagesAdapter);

Upvotes: 3

Eugen Pechanec
Eugen Pechanec

Reputation: 38223

It may not be obvious at first sight but don't try to cache or prepare fragments for a fragment adapter. Fragment(State)PagerAdapter.getItem has to return a new item and when the method is called is managed internally by the adapter.

Let the fragment adapter have data it needs to construct fragments at the right time:

final List<UserImage> userImages = user.getImages();
final ImagesPagerAdapter imagesAdapter = new ImagesPagerAdapter(getFragmentManager(), userImages);
viewPager.setAdapter(imagesAdapter);

And implement the fragment adapter so that getItem returns a new fragment:

public class ImagesPagerAdapter extends FragmentStatePagerAdapter {

    @NonNull
    private List<UserImage> userImages;

    public ImagesPagerAdapter(@NonNull final FragmentManager fragmentManager,
                              @NonNull final List<UserImage> userImages) {
        super(fragmentManager);
        this.userImages = userImages;
    }

    @Override
    public Fragment getItem(final int position) {
        final UserImage userImage = userImages.get(position);
        final SizeImage processed = userImage.getProcessed();
        return UserImageFragment.newInstance(processed.getFullsize())
    }

    @Override
    public int getCount() {
        return userImages.size();
    }
}

Here I see two options for improvement:

  1. Replace List<UserImage> with List<SizeImage> or List<whatever getFullSize returns>. The less implementation details you leak to the adapter the better.

  2. If userImage.getProcessed().getFullsize() takes time (accesses disk, performs computation) do that asynchronously in one of UserImageFragment lifecycle methods - defer to when it's needed and don't block UI thread.

Neither FragmentPagerAdapter nor FragmentStatePagerAdapter are built with updates in mind so

  1. Don't try to modify the userImages list.
  2. If the images change replace and reattach the whole adapter.

Upvotes: 0

Related Questions