How to implement a navigation controller within a child fragment? - android

I have a navigation controller fragment defined inside my activity_main that has a match height and width of parent. So when I navigate between fragments, it replaces the entire screen. Now say I navigate to a fragment that loads up a material card. I want that material card to have its own navigation controller that takes up the height and width of the material card. So when I use its navigation controller, it will only replace fragments within the material card view and not change the parent's view.
Is there a way to have two navigation host fragments? So I can define one inside activity_main and then another one that I can define inside the child fragment.
activity_main:
<androidx.constraintlayout.widget.ConstraintLayout
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="0dp"
app:defaultNavHost="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/barrier"
app:navGraph="#navigation/base_nav_graph" />
childfragment:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_layout"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="600dp"
android:layout_height="match_parent"
android:layout_gravity="end"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<fragment
android:id="#+id/nav_child_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:navGraph="#navigation/child_nav_graph" />

See advanced sample
https://github.com/android/architecture-components-samples/tree/master/NavigationAdvancedSample
There used multiple navigation controllers for BottomNavigationView items

Related

Bottom sheet fragments not showing when a FragmentContainerView is part of the layout

With the bottom sheet fragments not showing up, I initially thought the problem was within my Java code — maybe improper setup in the adapter, using the wrong fragment manager, etc — and not in the XML layouts. After a couple days of frustration, I finally identified the culprit.
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/nav_host_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="?android:attr/actionBarSize"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="#navigation/analytics"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/color_surface"
android:elevation="#dimen/size_2"
app:behavior_peekHeight="?android:attr/actionBarSize"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<com.google.android.material.tabs.TabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"/>
<androidx.viewpager.widget.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/tabs"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
When the bottom sheet is at collapsed, its tabs and their labels are visible (at the peekHeight), and I can tap through them. However, when the bottom sheet is expanded, the tabs are still visible but nothing is showing in their fragments. I've identified that if I comment out the FragmentContainerView — and here it really doesn't matter if I use the old fragment view instead — the bottom sheet works as expected, with all content in all tabs getting displayed.
So what's going on?
First, your TabLayout needs app:layout_constraintTop_toTopOf="parent"
And make ViewPager height other than 0dp like android:layout_height="match_parent" or 500dp .. something like that.
If that does not solve the issue you can use other layouts like RelativeLayout or LinearLayout as BottomSheet root.

Impossible to use DrawerLayout with dataBinding

I'm creating new app with MVVM architecture. So it has strong connection with dataBinding.
I want to use DrawerLayout and data Binding in MainActivity and here i have stuck, because rule for DrawerLayout root element in xml must be DrawerLayout and for success data binding, root element must be layout. Can somebody help me to combine this two root elements in correct way?
<?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">
<android.support.v4.widget.DrawerLayout
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<androidx.constraintlayout.widget.ConstraintLayout
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="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/actionBar_layout"
app:navGraph="#navigation/navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>
</android.support.v4.widget.DrawerLayout>
</layout>
Compiler told me that DrawerLayout can't be placed inside layout tag. If i put layout inside DrawerLayout then data binding is not working anymore
you should put the drawer layout in the layout tag, it works fine. your problem is that you are getting the drawer layout from android.support but your constraint layout is from androidx my suggestion is to migrate to androidx

How to replace a fragment using NavController.navigate instead of adding it on top

I'm using the "new" Android Jetpack navigation, instead of relying on FragmentManager. I've got a simple main layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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=".activities.MainActivity">
<FrameLayout
android:id="#+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/bottomNav">
<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_app" />
</FrameLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottomNav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="#menu/bottom_menu"
app:layout_constraintBottom_toBottomOf="#+id/root"/>
</android.support.constraint.ConstraintLayout>
And two additional layouts: "Splashscreen" with a TextView, "Login" with a button. The splashscreen fragment is used as starting point, and has a listener defined as follows:
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
textView.setOnClickListener{
findNavController(nav_host_fragment).navigate(R.id.action_splashScreenFragment_to_loginFragment)
}
}
screenshot
Instead of replacing TextView fragment with Button fragment, the Button fragment is stacked on top.
I've only encountered workarounds such as setting a background color, and clickable parameter, which I consider to be more of a hack, as I believe that the behaviour is exactly the same, but the fragment below is simply hidden. How to properly switch fragments using the NavController?
Issue seems to have occured because of these two lines:
val host = NavHostFragment.create(R.navigation.navigation_register_login)
supportFragmentManager.beginTransaction().replace(R.id.nav_host_fragment,host).setPrimaryNavigationFragment(host).commit()
I guess they shouldn't be called, and all basic requirements are fulfilled by this line in layout:
android:name="androidx.navigation.fragment.NavHostFragment"
You need to use popUpTo and popUpToInclusive to clear the backstack.
Check the Android Developers docs

How to ensure CoordinatorLayout children do not overlap

I am using bottom app bar inside the coordinator layout. the rest of the content is coming from a fragment. the fragment content is being covered by the bottom app bar
Because I am using bottomAppBar it has to be inside a CoordintorLayout and all the children have to be inside of it as well. All layout above the BottomAppBar should fill the space reminding, but if I do so the views at the bottoms get covered. how do I ensure the view do not overlap with BottomAppBar
<androidx.drawerlayout.widget.DrawerLayout
android:id="#+id/drawerLayout_main"
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=".main.MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="#+id/coordinatorLayout"
android:layout_width="0dp"
android:layout_height="wrap_content">
</androidx.constraintlayout.widget.ConstraintLayout>
<fragment
android:id="#+id/fragment_main_navHost"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
android:layout_gravity="top"
app:navGraph="#navigation/nav_graph"/>
<com.google.android.material.bottomappbar.BottomAppBar
android:id="#+id/bottomAppBar_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:fabAlignmentMode="end"
app:fabCradleMargin="4dp"
app:hideOnScroll="true"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView
android:id="#+id/navView_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="#layout/nav_header_layout"
app:menu="#menu/main_navigation"/>
What I need is to ensure whatever content is replacing the fragment should not be covered by the bottom app bar and it should use all the white space not being used the bottom app bar. I another world the content should be above it while using rest the white space and still be inside the coordinator layout.
Just add margin to your fragment container :
<fragment
android:id="#+id/fragment_main_navHost"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="{bottom_navigation_height}"
android:layout_marginTop="{tool_bar_height}"
app:defaultNavHost="true"
android:layout_gravity="top"
app:navGraph="#navigation/nav_graph"/>
According to the CoordinatorLayout Documentation, edges where a View in CoordinatorLayout expects to overlap with another View can be specified with layout_dodgeInsetEdges=”bottom”. So if your fragment is being overlapped by your BottomAppBar, you add this attribute to the fragment. You must also specify that you wish to dodge the BottomAppBar. This is done by adding layout_insetEdge=”bottom” to it.
To be more specific for your case, do the following:
Add app:layout_dodgeInsetEdges="bottom" to the <fragment> like so:
<fragment
app:layout_dodgeInsetEdges="bottom" <--- THIS
android:id="#+id/fragment_main_navHost"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
android:layout_gravity="top"
app:navGraph="#navigation/nav_graph"/>
Add app:layout_insetEdge="bottom" to BottomAppBar. (Note that this step my be unnecessary if BottomAppBar applies this inset by default so try also without it):
<com.google.android.material.bottomappbar.BottomAppBar
app:layout_insetEdge="bottom" <---- THIS
android:id="#+id/bottomAppBar_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:fabAlignmentMode="end"
app:fabCradleMargin="4dp"
app:hideOnScroll="true"/>
There's a really good article that shows examples with animated giffs that I highly recommend you to check out.
Try adding app:layout_behavior="#string/appbar_scrolling_view_behavior" to the fragment.

BottomNavigationView with activities

I am using BottomNavigationViewEx (BottomNavigationView with really good features). I have layout and now I want to 'connect' this navigation with my Activities. Usually it's connect with Fragments but my all activities extends AppCompatActivity.
Here's my nav.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.activity.RecipesActivity">
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#+id/navigation_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
<com.ittianyu.bottomnavigationviewex.BottomNavigationViewEx
android:id="#+id/navigation_view"
android:layout_width="0dp"
android:layout_height="65dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:background="#color/colorPrimary"
app:itemIconTint="#color/draw_item"
app:itemTextColor="#color/draw_item"
app:menu="#menu/nav_items" />
</android.support.constraint.ConstraintLayout>
How can I make this navigation visible on all activities and setup Click Listeners on items? I have five items on my navigation and I want to show for example activity 1 when I click on item 1 and so on.
I would recommend using Fragments for switching between tabs. Few pros when using Fragments:
It will prevent from destroying and creation of the activities and screen blinking.
It is easier to hold only one BottomNavigationView.
Single Responsibility for handling navigation, saving and restoring states.

Categories

Resources