When implementing a Master-Detail Flow with Navigation Components i don't see any possiblity to hide the detail fragment once a navGraph is set. Let's consinder a item in the Master View is selectable and deselectable. When deselcting an Item i cannot remove the StartDestination from the NavGraph.
Consider a Layout like this as the Master-Detail Flow Layout:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/itemList"
android:layout_width="400dp"
android:layout_height="match_parent"/>
<fragment
android:id="#+id/detailContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="false"/>
</LinearLayout>
As soon as i set a NavGraph with a startDestination to the detailContainer i cannot remove or hide it when i unselect the current selected item. The only solution i found so far was to to hide the container.
Related
I have an app, that has a few fragments in it. I'm using ViewPager 2 to swipe between them.
Lately I've added Navigation component to make a way to get into one specific fragment that is not avilable for user just by swipe.
I've found out, that whever I place FragmentContainerView over the ViewPager2 in my MainActivity layout, I can go to this unvailable fragment, but I can't swipe. Same thing whener I switch FragmentContainerView with ViewPager2. Is there any way to make them both work at the same time?
Here's my layout
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/fragmentContainerView"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph" />
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/viewPager"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
They can work together but not like this, at least not for what you want.
Navigation component is made to handle traveling between destinations. ViewPager2 is a widget to be used in a destination, not for traveling between them.
For what you are describing you will need to have FragmentA that will contain ViewPager2 where all of the swiping will take place and handling of fragments (let's say Fragment1, Fragment2, Fragment3) in the ViewPager2.
Those fragments will have nothing to do with the Navigation component.
Navigation component will come to play when you will want to go from FragmentA to FragmentB.
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
This is a simple example of a navigation setup that I can't get to work with the Navigation Component library, after researching for some time.
Let's say I have the following screen:
The sticky fragment at the top and the fragment at the bottom are in their own navigation graphs, here is the main_activity.xml:
<androidx.constraintlayout.widget.ConstraintLayout
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">
<fragment
android:id="#+id/nav_sticky_top"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="50dp"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/navigation_graph_sticky_top" />
<fragment
android:id="#+id/navigation_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="#id/nav_sticky_top"
app:navGraph="#navigation/navigation_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
When I press "navigate to sibling fragment" it navigates to the sibling fragment inside the bottom navigation graph, which is correct, the result:
This is the navigation_graph.xml:
<navigation 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/navigation_graph.xml"
app:startDestination="#id/blankFragment1">
<fragment
android:id="#+id/blankFragment1"
android:name="com.example.myapplication.BlankFragment1"
android:label="fragment_blank_fragment1"
tools:layout="#layout/fragment_blank_fragment1">
<action
android:id="#+id/action_blankFragment1_to_siblingFragment"
app:destination="#id/siblingFragment" />
</fragment>
<fragment
android:id="#+id/siblingFragment"
android:name="com.example.myapplication.SiblingFragment"
tools:layout="#layout/fragment_sibling_fragment" />
</navigation>
Now I would like to implement the "Navigate to fullscreen fragment" button, which should navigate to a fullscreen fragment which is in a separate, third navigation graph and should be above the sticky fragment navigation graph and the navigation graph below that. How can this be properly achieved? By that I mean without hacks like setting the visibility of the top navigation graph fragment to GONE and navigating in the bottom navigation graph to the fullscreen fragment.
UPDATE: I don't recommend this approach any more. For small apps it may work for you, but I ran into complications when trying to pass data between different navigation graphs hosted inside different NavHostFragments. I think the easiest route is the "hack" of hiding fragments in your top level layout to allow a full screen view. You can add an addOnDestinationChangedListener to listen for destinations that you want full screen, and simply hide the fragment required.
ORIGINAL ANSWER:
I do it like this in my app and it works well. You should wrap all your current fragments in a parent fragment with a main_nav_graph.xml. For example:
<androidx.constraintlayout.widget.ConstraintLayout
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">
<fragment
android:id="#+id/main_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/main_nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
Then your navigation for full screen fragments will look like this:
<navigation 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/main_nav_graph.xml"
app:startDestination="#id/main_fullscreen">
<fragment
android:id="#+id/main_fullscreen"
android:name="com.example.myapplication.MainFullscreen" >
<action
android:id="#+id/action_main_fullscreen_to_fullscreen2"
app:destination="#id/fullscreen2" />
</fragment>
<fragment
android:id="#+id/fullscreen2"
android:name="com.example.myapplication.Fullscreen2" />
</navigation>
Where the MainFullscreen has a layout like your ConstraintLayout you provided. Just make sure you add app:defaultNavHost="true" to all your child NavHostFragments.
Then when I want to navigate from your blankFragment1 to fullscreen2, I call this:
Navigation.findNavController(activity!!, R.id.main_host_fragment).navigate(R.id.action_main_fullscreen_to_fullscreen2)
Granted, there's not much point to having navigation graphs if they are not actually connected except via being in the same activity, but I just like the pretty picture of the app the graphs give you in the navigation editor :)
I have one drawer layout contains one fragment like below:
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/transparent">
<!-- The main content view -->
<FrameLayout
android:id="#+id/fl_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/gray_light_background_fragment" />
<!-- Drawer view -->
<fragment
android:id="#+id/navigation_drawer"
android:name="ui.fragment.drawer.NavigationDrawerFragment"
android:layout_width="#dimen/navigation_drawer_width"
android:layout_height="match_parent"
android:layout_gravity="start"
tools:layout="#layout/fragment_navigation_drawer_in_company_feed" />
</android.support.v4.widget.DrawerLayout>
I follow this https://developer.android.com/training/implementing-navigation/nav-drawer.html and can show drawer layout. But my drawer layout has just one view (layout)
I would like drawer layout can show 2 (or many) views (layouts) at many the different contexts. All views (layouts)is left side.
Example: At Activity A: drawer layout show view (layout) A.
At Activity B: drawer layout show view (layout) B.
Someone can help me. I look forward to your advice. Thanks you!
I have an activity whose root layout is a drawer layout, with main content being a view pager (and com.astuetz.viewpager.extensions.PagerSlidingTabStrip to assist viewpager. Now what i want to do is that when a particular item is selected in the navigation drawer, the view pager (along with the tab names strip) gets replaced by a single fragment. My activity layout file is this:
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
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" >
<com.astuetz.viewpager.extensions.PagerSlidingTabStrip
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="48dip"
android:background="#drawable/background_tab"
app:underlineColor="#E67E22"
app:indicatorColor="#E67E22" />
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/tabs"
tools:context=".UserActivity" >
</android.support.v4.view.ViewPager>
<FrameLayout android:id="#+id/fragmentToSwap"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
<ListView android:id="#+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="#666"
android:background="#E8E8E8"
/>
</android.support.v4.widget.DrawerLayout>
Now this is what i am trying to replace the view pager with fragment. I am not sure about this myself, a bit confused which element to put in replace method.
UserProfileActivity userProfileFragment = new UserProfileActivity();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragmentToSwap, userProfileFragment);
transaction.addToBackStack(null);
transaction.commit();
What currently is happening is the new fragment elements overlap the view pager while i am still able to use the view pager in background. Please help me figure it out.
The fragment overlaps the ViewPager because they are all in a RelativeLayout together - if you want to hide the ViewPager and the tab strip, you can call setVisibility(View.GONE) on each of them to hide them from view.
// assuming you do not already have a reference to them,
// find them by their ID and hide them
findViewById(R.id.tabs).setVisibility(View.GONE);
findViewById(R.id.pager).setVisibility(View.GONE);
Edit: Actually, you could place the ViewPager and tab strip in a fragment of its own, and attach that to the fragmentToSwap FrameLayout so that your fragment transaction would actually do the replacement - depending on how your app is architected, that may make more sense.