LightYearsBehind
LightYearsBehind

Reputation: 1794

ViewPager fragment with nested RecyclerView fragments using Android design support library

I want to use Android design support library to implement the following UI:

  1. Activity with a NavigationView for section navigation.
  2. When a section is selected, a new (parent) Fragment will be swapped into the container space (FrameLayout).
  3. The (parent) Fragment is always a CoordinatorLayout. In the layout, a ViewPager may be used. (*problem)
  4. If ViewPager is used for the (parent) Fragment, it will almost always accompanied by multiple child Fragment with RecyclerView. (*problem)

Problem

  1. When swiping on ViewPager, it doesn't swipe properly (eg. not 1 page per swipe).
  2. RecyclerView items are not shown (at all).

Activity layout

In Activity, there is a DrawerLayout with NavigationView and a FrameLayout for content fixing as follows:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true" />

    <android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        app:menu="@menu/activity_main_drawer" />

</android.support.v4.widget.DrawerLayout>

Fragment swap

Every time when a navigation item (section) is selected, new (parent) fragment is swapped into FrameLayout (R.id.container) with the following code:

getSupportFragmentManager()
    .beginTransaction()
    .replace(R.id.container, new ParentFragment())
    .commit();

Parent fragment (ViewPager)

In parent fragment, CoordinatorLayout is used together with AppBarLayout and ViewPager as follows:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.test.nestedviewpager.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

        <android.support.design.widget.TabLayout
            android:id="@+id/tab_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

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

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

</android.support.design.widget.CoordinatorLayout>

The code below initializes the ParentFragment with random number of pages.

package com.test.nestedviewpager;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

public class ParentFragment extends Fragment {
    private static final int MAX_PAGE_COUNT = 5;

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

        AppCompatActivity activity = (AppCompatActivity) getActivity();
        activity.setSupportActionBar((Toolbar) root.findViewById(R.id.toolbar));

        final ActionBar actionBar = activity.getSupportActionBar();
        if (actionBar != null) {
            actionBar.setHomeAsUpIndicator(R.drawable.ic_menu);
            actionBar.setDisplayHomeAsUpEnabled(true);
        }

        ViewPager viewPager = (ViewPager) root.findViewById(R.id.view_pager);
        viewPager.setAdapter(new MyPagerAdapter(activity.getSupportFragmentManager(), generatePages()));

        TabLayout tabLayout = (TabLayout) root.findViewById(R.id.tab_layout);
        tabLayout.setupWithViewPager(viewPager, true);

        return root;
    }

    private static Page[] generatePages() {
        Random rng = new Random();
        int size = rng.nextInt(MAX_PAGE_COUNT);
        if (size <=0 ) { size = 1; }
        Page[] pages = new Page[size];
        for (int pos = 0; pos < size; pos++) {
            pages[pos] = new Page("Page " + (pos + 1));
        }
        return pages;
    }

    private static class MyPagerAdapter extends FragmentPagerAdapter {
        private final List<Page> mPageList = new ArrayList<>();

        MyPagerAdapter(FragmentManager fm, Page[] pages) {
            super(fm);
            if ((pages != null) && (pages.length > 0)) {
                mPageList.addAll(Arrays.asList(pages));
            }
        }

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

        @Override
        public Fragment getItem(int position) {
            return new ChildFragment();
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return mPageList.get(position).title;
        }
    }

    private static class Page {
        final String title;

        Page(String title) {
            this.title = title;
        }
    }
}

Child fragment (RecyclerView)

In each child fragment, there consists of a RecyclerView as follows:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

The code below initializes the ChildFragment with random number of items.

package com.test.nestedviewpager;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

public class ChildFragment extends Fragment {
    private static final int MAX_ITEM_COUNT = 15;

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

        RecyclerView recyclerView = (RecyclerView) root.findViewById(R.id.recycler_view);
        recyclerView.setAdapter(new MyRecyclerAdapter(generateItems()));
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

        return root;
    }

    private static Item[] generateItems() {
        Random rng = new Random();
        final int size = rng.nextInt(MAX_ITEM_COUNT);
        if (size > 0) {
            Item[] items = new Item[size];
            for (int pos = 0; pos < size; pos++) {
                items[pos] = new Item("Item " + (pos + 1));
            }
            return items;
        }
        return null;
    }

    private static class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.MyViewHolder> {
        private final List<Item> mItemList = new ArrayList<>();

        MyRecyclerAdapter(Item[] items) {
            if ((items != null) && (items.length > 0)) {
                mItemList.addAll(Arrays.asList(items));
            }
        }

        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            View itemView = inflater.inflate(R.layout.recycler_item, parent, false);
            return new MyViewHolder(itemView);
        }

        @Override
        public void onBindViewHolder(MyViewHolder holder, int position) {
            holder.bind(mItemList.get(position));
        }

        @Override
        public int getItemCount() {
            return mItemList.size();
        }

        static class MyViewHolder extends RecyclerView.ViewHolder {
            private TextView mTitleView;

            MyViewHolder(View itemView) {
                super(itemView);
                mTitleView = (TextView) itemView.findViewById(R.id.content);
            }

            void bind(Item item) {
                mTitleView.setText(item.title);
            }
        }
    }

    private static class Item {
        final String title;

        Item(String title) {
            this.title = title;
        }
    }
}

I couldn't understand what is wrong with the code written at all. Any help would be very much appreciated.

Thank you.

Upvotes: 4

Views: 4636

Answers (1)

OneCricketeer
OneCricketeer

Reputation: 191725

I can't see a reason why the ViewPager would be skipping swipes, but for the Child Fragments, you should return root instead of the super call. That would explain why you see nothing

Maybe instead of activity.getSupportFragmentManager() try to use getChildFragmentManager() from the Fragment class

Upvotes: 1

Related Questions