18

So I've recently been working on updating my app to use the new material design support library. My application has one main activity with a drawerLayout and navigation view. The main content of app is shown in a frameLayout, through fragments. However, I am trying now to add material tabs to one of the navigation drawer's fragments. However, I am not sure how to implement this while keeping my fragments in the nav drawer functioning. A good example of what I am trying to achieve is shown below:

enter image description here

In this app (Google play music), only some of the navigation drawer's items have tabs while others do not. So my question is, how would I implement this? (Not looking for code, just an overview of how my layout should be organized)

To recap/clarify: I have a main layout with a frameLayout (for my app's content), and a navigationView (for navigating the different item fragments). I then have a listener which replaces the main layout's frameLayout with the item's respective fragment. Now, I need to add tabs to just one of these fragments (to navigate between 4 other fragments). I am also using a toolbar which I include as a separate layout.

Any advice is appreciated. I'm sorry if my description is a little confusing; I will clarify any necessary details.

mlz7
  • 2,067
  • 3
  • 27
  • 51

3 Answers3

7

Ok suppose your NavigationView has two options, the first one displays the fragment with tabs (tab layout) and the second one displays just a fragment with a toolbar. You have two options then:

  1. You can have a main layout with just a frame layout and replace it with all what you want
  2. You can have a main layout with coordinator layout -> app bar -> toolbar -> tab layout and a frame layout to put content

I prefer the second option to avoid having to always configure the toolbar so this is what I did once:

<!-- layout_main -->
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <android.support.design.widget.CoordinatorLayout
        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"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                app:layout_scrollFlags="scroll|enterAlways"/>

            <android.support.design.widget.TabLayout
                android:id="@+id/tab_layout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:visibility="gone"
                app:tabGravity="fill"
                app:tabMode="fixed" />

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

        <FrameLayout
            android:id="@+id/main_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />

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

    <!-- The NavigationView -->
    <fragment
        android:id="@+id/navigation_fragment"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:name="some.path.to.the.NavigationViewFragment"
        tools:layout="@layout/fragment_navigation_view" />

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

As you see I change the visibility of TabLayout to "gone" so that the fragment with tabs take care to set as visible. The Fragment with the tabs just have the ViewPager in the Layout:

<!-- fragment_with_tabs -->
<android.support.v4.view.ViewPager
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/view_pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Now the fragment with tabs initialize the ViewPager with the fragments for each page:

@Override 
public onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    // The getChildFragmentManager() is Very important! Because fragments inside fragments are
    // not supported with the tipical fragmentManager, it requires NestedFragments and those
    // uses a childFragmentManager(). In other case a strange behaviour occurs
    ViewPagerAdapter adapter = new ViewPagerAdapter(getChildFragmentManager());
    adapter.addFragment(new TabOneFragment(), "Tab 1");
    adapter.addFragment(new TabTwoFragment(), "Tab 2");
    viewPager.setAdapter(adapter);

    tabLayout = (TabLayout) getActivity().findViewById(R.id.tab_layout);
    tabLayout.setVisibility(View.VISIBLE);
    tabLayout.setupWithViewPager(viewPager);
}

And finally do whatever you want in your TabFragments, this works fine for me and I hope this be useful for you too. Sorry for some problem with code syntax, I develop android with Kotlin and not with Java.

Yiyo Castillo
  • 225
  • 2
  • 14
  • can you elaborate on how I would code the adapter? I tried creating a custom adapter and linking it as you showed but things do not seem to be working. – mlz7 Sep 03 '15 at 03:08
  • @Steve I can show you the Adapter I made in my Project, it's written in Kotlin but is easy to understand https://github.com/yiyocx/GitlabAndroid/blob/master/app/src/main/kotlin/yiyo/gitlabandroid/ui/adapters/ProjectsAdapter.kt – Yiyo Castillo Sep 04 '15 at 19:44
  • just out of curiosity, why do you develop in kotlin rather than java? – mlz7 Sep 04 '15 at 21:45
  • Because it's a very interesting language for Android, I really think it's better than Java because right now there is no way to use functional features of Java 8 but Kotlin has a lot of functional operations that makes easy the development – Yiyo Castillo Sep 07 '15 at 14:12
  • Mmm, with this approach, any fragment (even the one without tabs) will use that ```app:layout_behavior="@string/appbar_scrolling_view_behavior"```so even fragments without tabs will make the toolbar to scroll and hide. – Ferran Negre Dec 28 '15 at 23:19
3

I can't recommend what Yiyo suggested. If you are going to have Fragments with different layouts, you should let the Fragments customize these layouts in the XML. This is why the introduction of Toolbar made so much sense for Android development. In the future, you might even have more requirements that differ between each Fragment. Some of them might not want a Toolbar, some of them might need another View above the Toolbar, some of them will have a RecyclerView that you would like to be accessible to the CoordinatorLayout and AppBar so that the scrolling behavior works properly.

I recommend you to put only a FrameLayout as the content of your DrawerLayout (as Yiyo mentioned in point 1). Here you will load each Fragment from the callbacks of the NavigationView.

<android.support.v4.widget.DrawerLayout 
    ...
    android:fitsSystemWindows="true"
    >

    <FrameLayout
        android:id="@+id/drawer_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

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

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

In each Fragment's XML you will put, if required by that Fragment, a Toolbar. In your tabbed Fragment's XML you will put the TabLayout, and if you so wish, the CoordinatorLayout and AppBarLayout. From each Fragment that has a Toolbar, you will set the Toolbar as the ActionBar:

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

That's all there is to it. Of course you don't want to repeat yourself in every Fragment, so you can, for example, put this code in a DrawerFragment and subclass it for fragments with a Toolbar. You will also want to put your Toolbar XML configuration in a single file and include it in the Fragment's XML <include layout="@layout/toolbar" />. Or you might want to remove the Toolbar from some fragments, or change its color, theme, etc.

sorianiv
  • 4,845
  • 1
  • 23
  • 27
  • I think the toolbar must be present in all cases, we just need to add the tablayout for 1 navigationview item selected – Jey10 Jan 18 '16 at 09:26
  • @Jey10 That's good. You should still include the Toolbar in the Fragment, not in the Activity. Not only will that give you more flexibility, but it will allow you to easily get the CoordinatorLayout to work with RecyclerViews inside Fragments. – sorianiv Jan 18 '16 at 09:43
2

You can do it like this. I have checked by doing it myself and it works very well

Step 1 : Create a layout of your main activity like this

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

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar" android:layout_width="match_parent"
            android:layout_height="wrap_content" android:background="@color/blue"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            android:titleTextAppearance="@style/TextAppearance.AppCompat.Small"
            app:layout_scrollFlags="scroll|enterAlways" />

        <android.support.design.widget.TabLayout
            android:id="@+id/tab_layout" style="@style/FreeWiFiTabLayout"
            android:layout_width="match_parent" android:layout_height="wrap_content"
            android:layout_gravity="center" android:background="@color/blue"
            android:visibility="gone" android:fitsSystemWindows="true"
            android:minHeight="?attr/actionBarSize" />
    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/wifi_type_pager" android:layout_width="match_parent"
        android:layout_height="match_parent" android:clipToPadding="false"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
<fragment android:id="@+id/navigation_drawer"
    android:name="com.easyway.freewifi.NavigationDrawerFragment"
    android:layout_width="wrap_content" android:layout_height="match_parent"
    android:layout_gravity="start|bottom" android:layout_marginTop="?attr/actionBarSize"
    tools:layout="@layout/navigation_drawer_fragment" />

Step 2 :

In your activity you need to set onpagechangelistener on your viewpager:

 viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
  @Override
  public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

  }

  @Override
  public void onPageSelected(int position) {
        if(position ==0){
          tabLayout.setVisibility(View.GONE);
        }else{
          tabLayout.setVisibility(View.VISIBLE);
        }
  }

  @Override
  public void onPageScrollStateChanged(int state) {

  }
});

After this you need to add viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));

Step 3 :

This is how you can make your adapter for viewpager :

public class WiFiPagerAdapter extends FragmentPagerAdapter { private final List registeredFragments = new ArrayList<>();

public WiFiPagerAdapter(final FragmentManager fm) {
  super(fm);
}

@Override
public Fragment getItem(final int pos) {
  Fragment fragment;
  fragment = WiFiFragment.newInstance(pos);
  registeredFragments.add(pos, fragment);
  return fragment;
}

@Override
public int getCount() {
  return tabLayout.getTabCount();
}

public List<Fragment> getRegisteredFragmentsList() {
  return registeredFragments;
}

@Nullable
public Fragment getRegisteredFragment(final int position) {
  final Fragment wr = registeredFragments.get(position);
  if (wr != null) {
    return wr;
  } else {
    return null;
  }
}

@Override
public void notifyDataSetChanged() {
  super.notifyDataSetChanged();
  for (int i = 0; i < registeredFragments.size(); i++) {
    WiFiFragment wiFiFragment = ((WiFiFragment) registeredFragments.get(i));
    wiFiFragment.setWiFiFragmentRecyclerViewAdapter();
  }
}

}