Android: Navigation Component with Tab Layout

Viewed 4106

Can someone please confirm if we can use the Navigation Component of Android in Tab layout?

My main activity is as follows right now:

activity_main.xml

 <?xml version="1.0" encoding="utf-8"?>
    <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">

<androidx.coordinatorlayout.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.main.MainActivity">

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

        <com.google.android.material.appbar.MaterialToolbar
            android:id="@+id/Toolbar_Main"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:text="@string/app_name"
            app:layout_scrollFlags="scroll|enterAlways"
            app:titleTextAppearance="@style/Locky.Toolbar.TitleText"
            app:titleTextColor="@color/colorAccent" />

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/TabLayout_Main"
            style="@style/Widget.MaterialComponents.TabLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary"
            app:tabMode="fixed"
            app:tabGravity="fill"
            app:tabIconTint="@color/state_color_tabitem"
            app:tabIndicatorColor="@color/colorAccent" />

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

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/ViewPager_Main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end|end"
        android:orientation="vertical">

        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/FAB_Account"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:layout_marginEnd="16dp"
            android:layout_marginStart="16dp"
            android:visibility="invisible"
            app:srcCompat="@drawable/ic_account"
            style="@style/Locky.FloatingActionButton.Mini" />

        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/FAB_Card"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:layout_marginEnd="16dp"
            android:layout_marginStart="16dp"
            android:visibility="invisible"
            app:srcCompat="@drawable/ic_credit_card"
            style="@style/Locky.FloatingActionButton.Mini" />

        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/FAB_Add"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/fab_margin"
            android:layout_marginEnd="@dimen/fab_margin"
            android:layout_marginBottom="@dimen/fab_margin"
            app:srcCompat="@drawable/ic_add"
            style="@style/Locky.FloatingActionButton.Normal"/>

    </LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

</layout>

MainActivity.kt

package com.th3pl4gu3.locky.ui.main

import android.content.res.Configuration
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.animation.AnimationUtils
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import androidx.databinding.DataBindingUtil
import androidx.viewpager.widget.ViewPager
import com.th3pl4gu3.locky.R
import com.th3pl4gu3.locky.databinding.ActivityMainBinding
import com.th3pl4gu3.locky.ui.main.utils.activateDarkStatusBar
import com.th3pl4gu3.locky.ui.main.utils.activateLightStatusBar
import com.th3pl4gu3.locky.ui.main.utils.toast


class MainActivity : AppCompatActivity() {

private lateinit var _binding: ActivityMainBinding
private val tabIcons = intArrayOf(
    R.drawable.ic_home,
    R.drawable.ic_account,
    R.drawable.ic_credit_card,
    R.drawable.ic_settings
)

private var isOpen = false

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    //Bind the activity view.
    _binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

    //Update status bar upon current theme
    darkModeVerification()

    //Set toolbar as acton bar
    setSupportActionBar(_binding.ToolbarMain)

    /**
     * Setup for Tab layout, View Pager Adapter and Tab components
     **/
    val sectionsPagerAdapter = SectionsPagerAdapter(supportFragmentManager)
    setupViewPagerFragments(sectionsPagerAdapter)

    _binding.ViewPagerMain.adapter = sectionsPagerAdapter
    _binding.TabLayoutMain.setupWithViewPager(_binding.ViewPagerMain)
    setupTabIcons()

    _binding.ViewPagerMain.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() {
        override fun onPageSelected(position: Int) {
            when(position){
                0 -> _binding.FABAdd.show()
                1 -> _binding.FABAdd.show()
                2 -> _binding.FABAdd.show()
                3 -> _binding.FABAdd.hide()
            }
        }
    })

    //Load expandable FABs and animations
    loadFABs()
}

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    menuInflater.inflate(R.menu.menu_moreoptions_tab_main, menu)
    return true
}

override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
    R.id.Menu_Search -> {
        toast(getString(R.string.dev_feature_implementation_unknown, "Search"))
        true
    }

    else -> super.onOptionsItemSelected(item)
}

private fun loadFABs(){
    val fabOpen = AnimationUtils.loadAnimation(this, R.anim.anim_fab_open)
    val fabClose = AnimationUtils.loadAnimation(this, R.anim.anim_fab_close)
    val fabClockwise = AnimationUtils.loadAnimation(this, R.anim.anim_fab_rotate_clockwise)
    val fabAnticlockwise = AnimationUtils.loadAnimation(this, R.anim.anim_fab_rotate_anticlockwise)

    _binding.FABAdd.setOnClickListener {

        isOpen = if(isOpen){
            _binding.FABAdd.startAnimation(fabAnticlockwise)
            _binding.FABAccount.startAnimation(fabClose)
            _binding.FABCard.startAnimation(fabClose)
            false
        }else{
            _binding.FABAdd.startAnimation(fabClockwise)
            _binding.FABAccount.startAnimation(fabOpen)
            _binding.FABCard.startAnimation(fabOpen)

            true
        }

        _binding.FABAccount.setOnClickListener {
            toast(getString(R.string.dev_feature_implementation_unknown, "Account Creation"))
        }

        _binding.FABCard.setOnClickListener {
            toast(getString(R.string.dev_feature_implementation_unknown, "Card Creation"))
        }
    }
}

private fun darkModeVerification() = when (this.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
        Configuration.UI_MODE_NIGHT_YES -> window.activateDarkStatusBar()
        Configuration.UI_MODE_NIGHT_NO -> window.activateLightStatusBar(_binding.root)
        Configuration.UI_MODE_NIGHT_UNDEFINED -> window.activateLightStatusBar(_binding.root)
        else -> toast(getString(R.string.error_internal_code_1))
}

private fun setupViewPagerFragments(sectionsPagerAdapter: SectionsPagerAdapter) {
    sectionsPagerAdapter.addFragment(HomeFragment())
    sectionsPagerAdapter.addFragment(AccountFragment())
    sectionsPagerAdapter.addFragment(CardFragment())
    sectionsPagerAdapter.addFragment(SettingsFragment())
}

private fun setupTabIcons() = tabIcons.indices.forEach { x ->
    _binding.TabLayoutMain.getTabAt(x)!!.setIcon(tabIcons[x])
}
}

Can someone please explain how i can make use of Android Navigation Architecture to move between fragment instead of using ViewPager.

Thank you.

2 Answers

For viewpager solution you can refer to:

Suppose you don't want to use a view pager with tablayout (which was my case). It seems that there is no built-in functionality for this (https://www.youtube.com/watch?v=Y0Cs2MQxyIs) Presentantion slide from youtube

However, you can do a workaround like this for every time tab is selected:

// In tab selected listener 
when(position) {
 0 -> {
    navController.popBackStack()
    navController.navigate(R.id.CashRegister)
 }
 1 -> {
   navController.popBackStack()
   navController.navigate(R.id.OECSettings)
 }
}

Yes it is possible to use navigation component with tablayout. To implement tablayout and navigation component here is how you may go about it.

  1. you main activity just has fragmentContainerView for navHost.

     <androidx.constraintlayout.widget.ConstraintLayout 
       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=".activity.MainActivity">
    
       <androidx.fragment.app.FragmentContainerView
         android:id="@+id/nav_host_fragment"
         android:name="androidx.navigation.fragment.NavHostFragment"
         android:layout_width="0dp"
         android:layout_height="0dp"
         app:defaultNavHost="true"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintLeft_toLeftOf="parent"
         app:layout_constraintRight_toRightOf="parent"
         app:layout_constraintTop_toTopOf="parent"
         app:navGraph="@navigation/nav_graph" />
    
      </androidx.constraintlayout.widget.ConstraintLayout>
    
  2. You have a home fragment that has the tablayout and viewpager.

     <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/Theme.myapp.AppBarOverlay">
    
           <androidx.appcompat.widget.Toolbar
               android:id="@+id/toolbar"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               app:title="@string/app_name" />
    
           <com.google.android.material.tabs.TabLayout
               android:id="@+id/tabs"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:background="?attr/colorPrimary"/>
       </com.google.android.material.appbar.AppBarLayout>
    
       <androidx.viewpager2.widget.ViewPager2
           android:id="@+id/view_pager"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           app:layout_behavior="@string/appbar_scrolling_view_behavior" />
    
      </androidx.coordinatorlayout.widget.CoordinatorLayout>
    
  3. Now the tablayout is usually better placed inside an appbar but the main activity also has an app bar by default. so we can use a NoAction bar theme in the main activity. But now each fragment would have to implement its own action bar. In android manifest set theme of MainActivity

     <activity android:name=".activity.MainActivity"
         android:theme="@style/Theme.myapp.NoActionBar">
    
  4. In home fragment you connect tablayout and viewpager. I think these steps should be obvious if you follow android documentation for tablayout and viewpager. Hope this was helpful because this was not so straightforward for me.

Related