简体   繁体   中英

Navigation UI Architecture with collapsing toolbar in fragment

I am building my app with the modern Architecture Components.

Here is my main activity XML

<androidx.drawerlayout.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"
    tools:context=".MainActivity">

    <RelativeLayout
        android:id="@+id/relativeLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/main_app_bar_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <com.google.android.material.appbar.MaterialToolbar
                android:id="@+id/main_tool_bar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:theme="@style/MaterialTheme"
                style="@style/Widget.MaterialComponents.Toolbar.Primary"
                app:titleTextColor = "@android:color/white"
                />

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

        <fragment
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:defaultNavHost="true"
            app:navGraph="@navigation/navigation_graph"
            android:layout_below="@id/main_app_bar_layout"/>

    </RelativeLayout>


    <com.google.android.material.navigation.NavigationView
        android:id="@+id/navigation_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@drawable/gradient_background"
        android:theme="@style/Theme.Design"
        app:itemTextColor="@android:color/white"
        app:menu="@menu/drawer_menu" />

</androidx.drawerlayout.widget.DrawerLayout>

Here is my Main activity java file of how i am setting up my nav controller


        DrawerLayout drawerLayout = findViewById(R.id.drawer_layout);

        NavigationView navigationView = findViewById(R.id.navigation_view);

        MaterialToolbar mainToolBar = findViewById(R.id.main_tool_bar);

        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);

        AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(R.id.wallet_fragment)
                .setOpenableLayout(drawerLayout)
                .build();

        NavigationUI.setupWithNavController(mainToolBar, navController, appBarConfiguration);

Here is my fragment xml with the collapsing toolbar


<androidx.coordinatorlayout.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"
    tools:context=".fragments.WalletFragment">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/app_bar_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleTextAppearance="@style/TextAppearance.AppCompat.Large"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="5dp"
                android:padding="5dp">

                <de.hdodenhof.circleimageview.CircleImageView
                    android:id="@+id/imv_display_pic"
                    android:layout_width="120dp"
                    android:layout_height="120dp"
                    android:layout_alignParentStart="true"
                    android:layout_alignParentLeft="true"
                    android:layout_centerInParent="true"
                    android:src="@drawable/ic_launcher"
                    app:civ_border_color="@android:color/white"
                    app:civ_border_width="2dp" />

                <RelativeLayout
                    android:id="@+id/relative_layout"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_centerVertical="true"
                    android:layout_toEndOf="@+id/imv_display_pic"
                    android:layout_toRightOf="@+id/imv_display_pic">

                    <TextView
                        android:id="@+id/tv_wallet_balance"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginStart="10dp"
                        android:layout_marginLeft="10dp"
                        android:text="Ksh 1000"
                        android:textAppearance="@style/TextAppearance.AppCompat.Display1"
                        android:textColor="@android:color/white" />

                    <TextView
                        android:id="@+id/tv_display_wallet_balance"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_below="@id/tv_wallet_balance"
                        android:layout_marginStart="10dp"
                        android:layout_marginLeft="10dp"
                        android:text="Wallet Balance"
                        android:textAppearance="@style/TextAppearance.AppCompat.Medium" />

                </RelativeLayout>

            </RelativeLayout>

            <com.google.android.material.appbar.MaterialToolbar
                android:id="@+id/wallet_tool_bar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                app:title="Username" />

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

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

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipToPadding="false"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal">

            <com.google.android.material.button.MaterialButton
                android:id="@+id/btn_add_money"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="2dp"
                android:layout_marginLeft="2dp"
                android:layout_marginTop="2dp"
                android:layout_marginBottom="2dp"
                android:text="Add Money"
                android:textColor="@android:color/white"
                android:theme="@style/ButtonsTheme"
                app:icon="@drawable/ic_add_money"
                app:iconTint="@android:color/white"
                android:textAllCaps="false"/>

            <com.google.android.material.button.MaterialButton
                android:id="@+id/btn_withdraw_money"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="2dp"
                android:layout_marginLeft="2dp"
                android:layout_marginTop="2dp"
                android:layout_marginBottom="2dp"
                android:layout_toEndOf="@+id/btn_add_money"
                android:layout_toRightOf="@+id/btn_add_money"
                android:text="Withdraw Money"
                android:textColor="@android:color/white"
                android:theme="@style/ButtonsTheme"
                app:icon="@drawable/ic_withdraw"
                app:iconTint="@android:color/white"
                android:textAllCaps="false" />

        </RelativeLayout>

    </androidx.core.widget.NestedScrollView>


</androidx.coordinatorlayout.widget.CoordinatorLayout>

The issue am having is how will i make the material toolbar in the collapsing toolbar to be the main toolbar when the fragment is loaded right now its showing two toolbars the one in the main activity and the one in the fragment .

I have tried implementing the solution here: https://developer.android.com/guide/navigation/navigation-ui#support_app_bar_variations

Here is how i tried to implement in fragment java class

    NavController navController = Navigation.findNavController(view);
    AppBarConfiguration appBarConfiguration =
            new AppBarConfiguration.Builder(navController.getGraph()).build();
    MaterialToolbar walletToolBar = view.findViewById(R.id.wallet_tool_bar);

    NavigationUI.setupWithNavController(
            walletToolBar , navController, appBarConfiguration);

I have tried that but its still showing the toolbar from the main activity and from the fragment

I've been struggling with the same problem, and finally found the solution.

Firstly you need to remove the toolbar from your activity, and add separate one for every fragment what needs it, as stated in google documentation.

In your case, just remove toolbar declaration from your activity's layout:

<androidx.drawerlayout.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"
    tools:context=".MainActivity">

    <RelativeLayout
        android:id="@+id/relativeLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <fragment
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:defaultNavHost="true"
            app:navGraph="@navigation/navigation_graph"
            android:layout_below="@id/main_app_bar_layout"/>

    </RelativeLayout>
...

After that you need to link every fragment's toolbar with main navigation drawer, but before doing that you need to make some changes in activity.

Firstly remove the line NavigationUI.setupWithNavController(mainToolBar, navController, appBarConfiguration); from activity's onCreate method, because where will not be any toolbar created at the moment. Also you need to make your drawerLayout accessible from all fragments, so you need to make public variable for that(but you can make any other way you want, you just need simultaneous access to fragment's toolbar and activity's drawerLayout). In the end your activity's code should look like this:

public class MainActivity extends AppCompatActivity {
    public DrawerLayout drawerLayout = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        drawerLayout = findViewById(R.id.drawer_layout);
    }
    ...

And finally, to connect fragment's toolbar with activity's NavigationUI you need to add this piece of code to every fragment class(which can be cumbersome, I don't know about java, but in kotlin I did use extension functions to avoid that):

public class SomeFragment extends Fragment {
    ...

    // You may have used onActivityCreated instead
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        
        setupToolbar();
    }
    
    private void setupToolbar() {
        NavController navController = findNavController(R.id.nav_host_fragment);
        Toolbar toolbar = findViewById<Toolbar>(R.id.wallet_tool_bar);

        MainActivity activity = (MainActivity) getActivity();

        toolbar.setupWithNavController(navController, activity.drawerLayout);

        activity.setSupportActionBar(toolbar);
        activity.getActionBar().setDisplayHomeAsUpEnabled(true);
    }
    ...
}

The key part in this piece is toolbar.setupWithNavController(navController, drawerLayout); line, which links fragment's toolbar with activity's drawerLayout.

I didn't test this code because I use kotlin exclusively, so you should treat it like a representation rather than a complete code.

PS Sorry for my bad ingrish.

First we set up theme in themes.xml or styles.xml

    <style name="Theme.AppTheme" parent="Theme.MaterialComponents.DayNight">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/primaryColor</item>
        <item name="colorPrimaryVariant">@color/primaryDarkColor</item>
        <item name="colorOnPrimary">@color/primaryLightColor</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/secondaryColor</item>
        <item name="colorSecondaryVariant">@color/secondaryDarkColor</item>
        <item name="colorOnSecondary">@color/secondaryLightColor</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
        <item name="actionBarTheme">@style/ActionBarTheme</item>

    </style>

    <style name="ActionBarTheme" parent="Theme.MaterialComponents.DayNight">

        <item name="colorPrimary">@color/primaryColor</item>
        <item name="colorPrimaryVariant">@color/primaryDarkColor</item>
        <item name="colorOnPrimary">@color/primaryLightColor</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/secondaryColor</item>
        <item name="colorSecondaryVariant">@color/secondaryDarkColor</item>
        <item name="colorOnSecondary">@color/secondaryLightColor</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>

        <!--Toolbar text color-->
        <item name="android:textColorPrimary">@android:color/white</item>

        <!-- action menu item icon color -->
        <item name="colorControlNormal">@android:color/white</item>

    </style>

Then in Manifest inside application tag


android:theme="@style/Theme.AppTheme"

MainActivity XML Implementation

<androidx.drawerlayout.widget.DrawerLayout 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"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

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

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

    </RelativeLayout>

    <!--app:headerLayout="@layout/nav_header"-->
    <com.google.android.material.navigation.NavigationView
        android:id="@+id/navigation_view"
        android:layout_width="230dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@drawable/nav_view_background"
        android:fitsSystemWindows="true"
        app:itemTextColor="@android:color/white"
        app:itemIconTint="@android:color/white"
        android:theme="@style/Theme.AppCompat.DayNight"
        app:menu="@menu/drawer_menu">

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


</androidx.drawerlayout.widget.DrawerLayout>

MainActivity java file implementation


    DrawerLayout drawerLayout;
    NavigationView navigationView;
    AppBarConfiguration appBarConfiguration;
    NavController navController;

    ActionBarDrawerToggle navDrawerToggle;

   // Inside onCreate method 

        drawerLayout = findViewById(R.id.drawer_layout);

        navigationView = findViewById(R.id.navigation_view);

        navController = Navigation.findNavController(this, R.id.nav_host_fragment);

        navDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, "open", "close");

        drawerLayout.addDrawerListener(navDrawerToggle);
        navDrawerToggle.setDrawerIndicatorEnabled(true);
        navDrawerToggle.syncState();

        appBarConfiguration = new AppBarConfiguration.Builder(R.id.nav_wallpapers_fragment)
                .setOpenableLayout(drawerLayout)
                .build();

        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);

        NavigationUI.setupWithNavController(navigationView, navController);

Fragment Having the Collapsing Toolbar XML implementation

<androidx.coordinatorlayout.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:background="@color/colorWhiteShade"
    tools:context=".fragments.WallpaperDetailsFragment">

    <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/app_bar_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fitsSystemWindows="true"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

    <!--        Change height of the collapsing toolbar here-->
            <!--To make the expanded title appearance to transparent-->
    <!--        app:expandedTitleTextAppearance="@android:color/transparent"-->
            <com.google.android.material.appbar.CollapsingToolbarLayout
                android:id="@+id/collapsing_toolbar_layout"
                android:layout_width="match_parent"
                android:layout_height="200dp"
                app:contentScrim="?attr/colorPrimary"
                android:fitsSystemWindows="true"
                app:layout_scrollFlags="scroll|exitUntilCollapsed"
                app:expandedTitleTextAppearance="@android:color/transparent">

                <ImageView
                    android:id="@+id/imageview"
                    android:layout_width="match_parent"
                    android:layout_height="200dp"
                    android:scaleType="centerCrop"
                    android:contentDescription="@string/wallpaper_collapsing_toolbar"/>

                <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/ThemeOverlay.AppCompat.Light"
                    app:title="Toolbar Title"/>

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

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

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clipToPadding="false"
        android:scrollbars="none"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
      
            // Create your views here

        </RelativeLayout>

    </androidx.core.widget.NestedScrollView>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

Fragment having the collapsing toolbar

    AppBarLayout appBarLayout;

    CollapsingToolbarLayout collapsingToolBarLayout;

    Toolbar toolbar;

    DrawerLayout drawerLayout;

    // In onCreate method

     @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Objects.requireNonNull(((MainActivity) requireActivity()).getSupportActionBar()).hide();

    }

   // In onViewCreated method 

        appBarLayout= view.findViewById(R.id.app_bar_layout);
        CollapsingToolbarLayout = view.findViewById(R.id.collapsing_toolbar_layout);
        toolbar= view.findViewById(R.id.toolbar);

         AppBarConfiguration appBarConfiguration =
                new AppBarConfiguration.Builder(navController.getGraph()).build();

        NavigationUI.setupWithNavController(
                toolbar, navController, appBarConfiguration);

        NavigationUI.setupWithNavController(CollapsingToolbarLayout , toolbar, navController);



The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM