I am starting to work on Android application and I would like to use this new Android navigation component. And this application will have bottom navigation view with 5 sections, and each of those sections needs to keep backstack of fragments. Like if I am on First Section and navigate to search results and then navigate to map, and now if I click on profile section in bottom navigation view then return to first section I still need to be at the map screen. So I found this medium article that explains one possible solution to this problem which is to have 5 NavHostFragments and you show/hide them based on which section you are. Also another thing is that when you navigate to some fragments I need to hide bottom navigation view... So I created new project to try some things out and I got some strange results. I can't fully understand how fitsSystemWindows attribute work and I got some weird behaviors...
First thing is that the title in the toolbar at the 4th image has some top margin and I have no idea why... I have created completely new project without fragments, just one main activity with exact same layout and I don't have top margin for title in toolbar.
The second thing is that when I get back from Safari page to search results (4th and 5th images), bottom navigation view is half visible for no reason...
How do I fix 4th and 5th images?
Also one more question regarding fitSystemWindows attribute. Shouldn't Search button on the first image be under the status bar? Since fitSystemWindows=true is not applied on ConstraintLayout -> FrameLayout -> ExploreFragment's ConstraintLayout. The only place where fitSystemWindows attribute is added to XML is in fragment_safari.xml file.
Note: Minimum Android API level that I need to support is 21 (Lollipop and above).
Here is the code:
Theme that I am using
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowBackground">@color/colorWindowBackground</item>
</style>
MainActivity.kt
class MainActivity : AppCompatActivity() {
// ...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
currentController = findNavController(R.id.explore_nav_host_fragment)
exploreNavController.addOnNavigatedListener { controller, destination ->
when (destination.id) {
R.id.searchResultsFragment,
R.id.exploreFragment -> showBottomNavigation()
R.id.safariFragment,
R.id.filterFragment -> hideBottomNavigation()
}
}
bottom_navigation.setOnNavigationItemSelectedListener {
when (it.itemId) {
R.id.action_explore -> {
currentController = exploreNavController
explore_nav_host_frame.visibility = View.VISIBLE
favorite_nav_host_frame.visibility = View.GONE
reviews_nav_host_frame.visibility = View.GONE
true
}
R.id.action_favorite -> {
currentController = favoriteNavController
explore_nav_host_frame.visibility = View.GONE
favorite_nav_host_frame.visibility = View.VISIBLE
reviews_nav_host_frame.visibility = View.GONE
true
}
else -> false
}
}
}
fun hideBottomNavigation() {
if(isBottomNavigationViewVisible) {
bottom_navigation.visibility = View.GONE
isBottomNavigationViewVisible = false
}
}
fun showBottomNavigation() {
if(!isBottomNavigationViewVisible) {
bottom_navigation.visibility = View.VISIBLE
isBottomNavigationViewVisible = true
}
}
// ...
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:id="@+id/explore_nav_host_frame"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/bottom_navigation"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<fragment
android:id="@+id/explore_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="false"
app:navGraph="@navigation/nav_graph_explore" />
</FrameLayout>
<FrameLayout
android:id="@+id/favorite_nav_host_frame"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/bottom_navigation"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<fragment
android:id="@+id/favorite_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="false"
app:navGraph="@navigation/nav_graph_favorite" />
</FrameLayout>
<FrameLayout
android:id="@+id/reviews_nav_host_frame"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/bottom_navigation"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<fragment
android:id="@+id/reviews_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="false"
app:navGraph="@navigation/nav_graph_reviews" />
</FrameLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:background="@android:color/darker_gray"
android:elevation="4dp"
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="@menu/bottom_menu"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
SafariFragment.kt
class SafariFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_safari, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
initActionBar()
addTextViews()
}
private fun initActionBar() {
(activity as AppCompatActivity).apply {
setSupportActionBar(toolbar)
supportActionBar?.apply {
title = "Safari"
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)
}
}
}
private fun addTextViews() {
for(i in 1..10) {
content_scroll.addView(TextView(context).apply {
text = i.toString()
setBackgroundColor(ContextCompat.getColor(context, R.color.colorAccent))
setTextColor(ContextCompat.getColor(context, R.color.white))
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
48*3
).apply {
setMargins(36, 36, 36, 36)
}
})
}
}
}
fragment_safari.xml
<?xml version="1.0" encoding="utf-8"?>
<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:fitsSystemWindows="true">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height="240dp"
android:fitsSystemWindows="true"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed|snap">
<ImageView
android:id="@+id/safari_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"
app:srcCompat="@drawable/safari"
tools:ignore="ContentDescription" />
<View
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="@drawable/toolbar_gradient"
android:fitsSystemWindows="true" />
<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" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:id="@+id/content_scroll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
EDIT:
I have managed to fix bottom navigation view, what I did is put CoordinatorLayout as the root for each fragment's layout and then applied fitsSystemWindows=true to CoordinatorLayout and to the first child as well. But still I am getting issue with title in the toolbar.
