Coordinator Layout with Toolbar in Fragments or Activity

AndroidAndroid FragmentsAndroid ActivityAndroid Actionbar

Android Problem Overview


With the new design library there are several new layouts that change a lot how the toolbar can behave if the developer so wishes. Since different fragments have different behaviors and objectives, for example a gallery fragment with a collapsing toolbar showing an important photo, or a fragment without a scrollview that just doesn't need the appbarlayout for hiding the toolbar, having a single toolbar in the activity can prove difficult.

So with this, should I move the toolbar to each fragment? If so, I have to set the supportActionBar each time I show a fragment and also have a reference of the activity in the fragment which nullifies the independent nature of fragments. If I leave the toolbar in the Activity alone, I have to have multiple layouts defined for each type of behavior in each fragment. What would be the best approach?

Android Solutions


Solution 1 - Android

As for me it sounds too weird to have appbar and toolbar in each fragment. So I've chosen to have single appbar with toolbar in activity.

To solve that issue with CoordinatorLayout you will have to set different behaviour of your FrameLayout (or any other Layout) that supposed to hold fragments from each fragment that you want to override default behaviour.

Lets assume, that your default behaviour is app:layout_behavior="@string/appbar_scrolling_view_behavior"

Then in your fragment_activity_layout.xml you may have something like that:

<android.support.design.widget.CoordinatorLayout
	android:id="@+id/coordinator"
	android:layout_width="match_parent"
	android:layout_height="match_parent">

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

		<android.support.v7.widget.Toolbar
			android:id="@+id/dashboard_toolbar"
			android:layout_width="match_parent"
			android:layout_height="wrap_content"
			android:theme="@style/AppTheme.Toolbar"
			app:layout_scrollFlags="scroll|enterAlways"/>
	</android.support.design.widget.AppBarLayout>

	<FrameLayout
		android:id="@+id/dashboard_content"
		android:layout_width="match_parent"
		android:layout_height="match_parent"
		app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>

And in each fragment you wish not to implement app:layout_behavior="@string/appbar_scrolling_view_behavior" you will have to override onAttach and onDetach methods that will change behaviour of your FrameLayout:

CoordinatorLayout.Behavior behavior;

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    if(behavior != null)
        return;

    FrameLayout layout =(FrameLayout) getActivity().findViewById(R.id.dashboard_content);
    CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) layout.getLayoutParams();

    behavior = params.getBehavior();
    params.setBehavior(null);

}

@Override
public void onDetach() {
    super.onDetach();
    if(behavior == null)
        return;

    FrameLayout layout =(FrameLayout) getActivity().findViewById(R.id.dashboard_content);
    CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) layout.getLayoutParams();

    params.setBehavior(behavior);

    layout.setLayoutParams(params);

    behavior = null;
}

After that CoordinatorLayout won't collapse appbar, etc. and will allow fragment layouts to be full-height.

Solution 2 - Android

Here's my solution

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">

<!-- Put your fragment inside a Framelayout and set the behavior for this FrameLayout -->
<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <!-- Your fragment -->
    <include layout="@layout/content_main" />

</FrameLayout>

<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:layout_scrollFlags="scroll|enterAlways"
        app:popupTheme="@style/AppTheme.PopupOverlay" />

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

Solution 3 - Android

This is a realy good question: should Toolbars that need to act like an ActionBar be kept in a Activity or a Fragment? Having searched around different questions and documentation, I couldn't find a solution that covers all cases. It therefore really depends on your situation wich way to go.

Case 1: Toolbar must be ActionBar replacement

If the Toolbar has to behave like a normal ActionBar (or if max 1 fragment is shown from time to time), I think the best/simplest way is to use traditional Activities with there own Toolbar and put your Fragment in there. This way you don't have to worry about when which Toolbar must be shown.

Changing the ActionBar (-behaviour) from Fragments is also possible, but I would not recommend it, since that forces you to keep track which Fragment changed the ActionBar when. I don't even know if setting the ActionBar can be done multiple times.

Case 2: Each Fragment should have its own (part of) Toolbar

You could also choose to put different stand alone Toolbars in different Fragments, with their own actions. This way you could display different Fragments next to each other - each with their own actions in their Toolbar - and suggest that it is 1 Toolbar (maybe like the Gmail-app, although I am unsure). This however means that you would have to inflate those Toolbars yourself, but it must not be very difficult.

I hope this will help making a choice.

(Sorry if I made any (language-)mistakes)

Solution 4 - Android

That's what the navigation documentation says:

> Adding the top app bar to your activity works well when the app bar’s layout is similar for each destination in your app. If, however, your top app bar changes substantially across destinations, then consider removing the top app bar from your activity and defining it in each destination fragment, instead.

Actually it's very easy to setup a Toolbar using NavigationUI, and this solution doesn't require the Fragment to have any knowledge about its parent. For example:

<!-- my_fragment.xml -->
<androidx.constraintlayout.widget.ConstraintLayout ...>

  <com.google.android.material.appbar.MaterialToolbar
    android:id="@+id/toolbar"
    ... />

</androidx.constraintlayout.widget.ConstraintLayout>
class MyFragment : Fragment() {
  ...

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    val navController = findNavController()
    binding.toolbar.setupWithNavController(navController)
  }
}

You can find the full GitHub example here. There's also a relative question that might be interested Is setSupportActionbar required anymore?

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmthandrView Question on Stackoverflow
Solution 1 - AndroidКлаус ШварцView Answer on Stackoverflow
Solution 2 - AndroidCharles-Eugene LoubaoView Answer on Stackoverflow
Solution 3 - AndroidCoen BView Answer on Stackoverflow
Solution 4 - AndroidValeriy KatkovView Answer on Stackoverflow