haike haike - 2 months ago 24
Android Question

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.

Answer

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