-1

I'm using android navigation controller.

My main activity hosts a nav fragment but where should other components like toolbars and nav bottom bar go, in the main activity or child fragments?

activity_main.xml

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

     <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:layout_collapseMode="pin"
        app:popupTheme="@style/AppTheme.PopupOverlay" />

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        app:defaultNavHost="true"
        app:navGraph="@navigation/navigation" />

    <android.support.design.widget.BottomNavigationView
        android:id="@+id/bottom_nav_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:menu="@menu/menu_bottom_nav" />

</LinearLayout>

fragment_home.xml

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

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerList"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical" />
</RelativeLayout>

or should ToolBar and BottomNavigationView be in fragment_home.xml like so

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

     <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:layout_collapseMode="pin"
        app:popupTheme="@style/AppTheme.PopupOverlay" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerList"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical" />

    <android.support.design.widget.BottomNavigationView
        android:id="@+id/bottom_nav_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:menu="@menu/menu_bottom_nav" />
</RelativeLayout>
ir2pid
  • 5,604
  • 12
  • 63
  • 107

1 Answers1

2

I'll supply you with an example. Keep in mind, I'm compiling with the latest androidx for SDK 28+ so if you are on older, your namespaces will be slightly different.

I'm also using Databinding and Kotlin, so don't use the layout and data tags if you are not using databinding.

TOOLBAR

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

<data>
     PUT BINDING VARIABLES HERE
</data>

<androidx.coordinatorlayout.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/SSTheme.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?android:attr/actionBarSize"
            android:theme="@style/ToolbarTextAppearance">

        </androidx.appcompat.widget.Toolbar>

    </com.google.android.material.appbar.AppBarLayout>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

MAIN ACTIVITY

   <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable name="activity" type="com.a35.activities.MainActivity"/>
        <variable name="iBindingRecyclerView" type="com.a35.interfaces.IBindingRecyclerView"/>
    </data>

    <androidx.drawerlayout.widget.DrawerLayout
        android:id="@+id/drawerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="false"
        tools:openDrawer="start">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <!--TOP TOOLBAR-->
            <include
                android:id="@+id/toolbarMain"
                layout="@layout/toolbar_main"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>

            <!--TOP BLACK LINE-->
            <View
                android:id="@+id/vRedLine"
                android:layout_width="match_parent"
                android:layout_height="@dimen/dp_2"
                android:background="@color/black" />

            <!-- FrameLayout is used to insert fragments to display -->
            <FrameLayout
                android:id="@+id/fragPlaceholder"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:layout_behavior="@string/appbar_scrolling_view_behavior" />

        </LinearLayout>

        <com.google.android.material.navigation.NavigationView
            android:id="@+id/nav_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            android:fitsSystemWindows="true"
            android:background="@color/colorPrimary">

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <include layout="@layout/nav_drawer_header"
                         android:id="@+id/navHeader"/>

                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/lstMenuItems"
                    android:layout_below="@+id/navHeader"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    app:bindRcvInterface="@{iBindingRecyclerView}"
                    app:bindRcvList="@{activity.getDrawerItemList}"/>

                <ImageView
                    android:id="@+id/imgBottomLogo"
                    android:layout_width="@dimen/dp_160"
                    android:layout_height="@dimen/dp_35"
                    android:layout_alignParentBottom="true"
                    android:layout_centerHorizontal="true"
                    android:layout_marginBottom="@dimen/dp_35"
                    android:src="@drawable/scott_logo" />

            </RelativeLayout>

        </com.google.android.material.navigation.NavigationView>

    </androidx.drawerlayout.widget.DrawerLayout>

</layout>

Notice the Main Activity holds Navigation View that is inside a drawer layout which contains the Recycler View for building your drawer content.

Outside of that you will see the content placeholder for fragments called fragPlaceHolder.

Lastly notice the parent layout is a LinearLayout with the first element being the include for the toolbar to include as we see fit.

Next your Styles. You'll need to use a style that does not rely on the action bar if you plan to use the toolbar as the actionbar. (for the record, you should be using the toolbar)

STYLE

   <!--Full Screen-->
<style name="A35.FullScreen">
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
    <item name="android:windowFullscreen">true</item>
</style>

MANIFEST

  <application
    android:name=".application.A35Application"
    android:allowBackup="true"
    android:icon="@mipmap/a35_logo"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/A35.FullScreen">

You can also put it on individual Activities if that fits your need better.

Now you have two steps left.

You need to call setActionToolbar and pass your found UI or use synthetic views if you are using kotlin and databinding that is simple to do. So add in your onCreate AFTER inflating the view.

setSupportActionBar(toolbar)

You didn't ask this, but I'll supply it as well. When you need to change out your fragment, I typically have a BaseActivity with a helper method that does it like so.

 protected fun swapFragment(fragment: BaseFragment, @Nullable bundle: Bundle?, hideCurrentFrag: Boolean = false) {
    if (fragment.isVisible) {
        A35Log.e(mClassTag, "swapFragment called on already visible fragment")
        return
    }

    A35Log.v(mClassTag, "swapFragment( ${fragment.javaClass.simpleName} )")
    val currentFragBundle = fragment.arguments
    if (currentFragBundle == null && bundle != null) {
        fragment.arguments = bundle
        A35Log.v(mClassTag, "current bundle is null, so setting new bundle passed in")
    } else if (bundle != null) {
        fragment.arguments?.putAll(bundle)
        A35Log.v(mClassTag, "current fragment bundle was not null, so add new bundle to it")
    }

    val fragmentManager = supportFragmentManager
    fragmentManager.executePendingTransactions()
    val fragmentTransaction = fragmentManager.beginTransaction()

    //Make sure the requested fragment isn't already on the screen before adding it
    if (fragment.isAdded) {
        A35Log.v(mClassTag, "Fragment is already added")
        if (fragment.isHidden) {
            A35Log.v(mClassTag, "Fragment is hidden, so show it")
            fragmentTransaction.show(fragment)
            if(hideCurrentFrag) {
                A35Log.v(mClassTag, "hideCurrentFlag = true, hiding current fragment $mSelectedFragment")
                fragmentTransaction.hide(mSelectedFragment!!)
            }else{
                A35Log.v(mClassTag, "hideCurrentFlag = false, removing current fragment $mSelectedFragment")
                fragmentTransaction.remove(mSelectedFragment!!)
            }
        }else{
            A35Log.v(mClassTag, "Fragment is already visible")
        }
    }else if(mSelectedFragment == null){
        A35Log.v(mClassTag,"mSelectedFragment = null, so replacing active fragment with new one ${fragment.javaClass.simpleName}")
        fragmentTransaction.replace(R.id.fragPlaceholder, fragment)
    }else{
        A35Log.v(mClassTag, "Fragment is not added, so adding to the screen ${fragment.javaClass.simpleName}")
        fragmentTransaction.add(R.id.fragPlaceholder, fragment)
        if(hideCurrentFrag) {
            A35Log.v(mClassTag, "hideCurrentFlag = true, hiding current fragment $mSelectedFragment")
            fragmentTransaction.hide(mSelectedFragment!!)
        }else{
            A35Log.v(mClassTag, "hideCurrentFlag = false, removing current fragment $mSelectedFragment")
            fragmentTransaction.remove(mSelectedFragment!!)
        }
    }

    A35Log.v(mClassTag, "committing swap fragment transaction")
    fragmentTransaction.commit()
    A35Log.v(mClassTag, "mSelectedFragment = ${fragment.javaClass.simpleName}")
    mSelectedFragment = fragment
}

NOTE* While swapping fragments is fairly universal, you need to make sure you are handling your needs properly. Should it be hiding, or removing. Should it be handling the bundles or ignoring them. The method I supplied you is basically showing if non-existent and hiding fragment instead of removing if told to hide it.

Handling building an adapter and list for the nav drawer and wiring up listener clicks on those items is not shown here as that was not your question and is beyond the scope of this question. I don't want to make my answer too bloated, so hopefully that is all you need to get going.

Happy Coding.

Sam
  • 5,342
  • 1
  • 23
  • 39