Android - mixing NavGraph with ViewPager2 - java

I'm looking for the following design in my android app:
I want the main screen to be a TabLayout with 3 tabs, and a ViewPager2.
From each one of the 3 fragments (Fragment1, Fragment2, Fragment3) corresponding to the tabs, there are some actions the user can do to navigate to other fragments--those don't have the TabLayout and ViewPager2. Those nav actions are handled by a nav_graph.
Currently, this is what I'm working with:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:id="#+id/main_activity_coordinator"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
app:layout_constraintTop_toTopOf="parent"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:id="#+id/app_bar"
>
<!-- toolbar -->
</com.google.android.material.appbar.AppBarLayout>
<androidx.fragment.app.FragmentContainerView
android:tag="MAIN-FRAG"
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="70dp"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
main_fragment.xml (this contains the TabLayout and ViewPager2)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:tag="MAIN-FRAG"
android:id="#+id/main_relative_layout"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:paddingTop="?attr/actionBarSize"
xmlns:tools="http://schemas.android.com/tools">
<com.google.android.material.tabs.TabLayout
android:id="#+id/tabBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<com.google.android.material.tabs.TabItem
...
/>
<com.google.android.material.tabs.TabItem
...
/>
<com.google.android.material.tabs.TabItem
...
/>
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager2.widget.ViewPager2
android:layout_below="#id/tabBar"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
/>
</RelativeLayout>
MainFragment.java
public class MainFragment extends Fragment {
private ViewPager2 viewPager;
private NavigationAdapter navigationAdapter;
private MainFragmentBinding binding;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().setContentView(R.layout.main_fragment);
TabLayout tabLayout = getActivity().findViewById(R.id.tabBar);
int[] tabTexts = {
R.string.tab1,
R.string.tab2,
R.string.tab3
};
// set up view pager and attach mediator to tab layout
this.viewPager = getActivity().findViewById(R.id.pager);
this.navigationAdapter = new NavigationAdapter(this);
viewPager.setAdapter(this.navigationAdapter);
new TabLayoutMediator(tabLayout, viewPager,
(tab, position) -> tab.setText(
tabTexts[position]
)
).attach();
}
}
nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<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/nav_graph"
app:startDestination="#id/Fragment1">
<fragment
android:id="#+id/Fragment1"
android:name="com.my_app..fragments.MainFragment"
tools:layout="#layout/main_fragment">
<action
android:id="#+id/action_Fragment1_to_FragmentX"
app:destination="#id/FragmentX" />
</fragment>
<fragment
android:id="#+id/FragmentX"
android:name="com.my_app.fragments.FragmentX"
tools:layout="#layout/fragment_x">
</fragment>
<fragment
android:id="#+id/Fragment2"
android:name="com.my_app.fragments.ExercisesFragment"
tools:layout="#layout/fragment_2">
<action
android:id="#+id/action_Fragment2_to_FragmentY"
app:destination="#id/FragmentY" />
</fragment>
<fragment
android:id="#+id/FragmentY"
android:name="com.my_app.fragments.ExerciseDetailFragment"
tools:layout="#layout/fragment_y">
</fragment>
</navigation>
In other words, the TabLayout handles Fragment1, Fragment2, and Fragment3 (not shown in the nav graph because it doesn't have outward actions). From Fragment1, you can navigate to FragmentX (which doesn't not have the tablayout) and from Fragment2 to FragmentY.
This seems to be working, but if I rotate my screen I get the following error:
2022-05-06 11:09:00.009 11445-11445/com.my_app E/FragmentManager: Fragment no longer exists for key f#0: unique id ba9898fb-18f8-4eaa-a250-97c08a896379
2022-05-06 11:09:00.009 11445-11445/com.my_app E/FragmentManager: Activity state:
2022-05-06 11:09:00.031 11445-11445/com.my_app E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.my_app, PID: 11445
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.my_app/com.my_app.MainActivity}: java.lang.IllegalStateException: Fragment no longer exists for key f#0: unique id ba9898fb-18f8-4eaa-a250-97c08a896379
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2827)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2902)
at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4784)
at android.app.ActivityThread.-wrap18(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1609)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:169)
at android.app.ActivityThread.main(ActivityThread.java:6578)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: java.lang.IllegalStateException: Fragment no longer exists for key f#0: unique id ba9898fb-18f8-4eaa-a250-97c08a896379
at androidx.fragment.app.FragmentManager.getFragment(FragmentManager.java:975)
at androidx.viewpager2.adapter.FragmentStateAdapter.restoreState(FragmentStateAdapter.java:549)
at androidx.viewpager2.widget.ViewPager2.restorePendingState(ViewPager2.java:350)
at androidx.viewpager2.widget.ViewPager2.dispatchRestoreInstanceState(ViewPager2.java:375)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3729)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3729)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3729)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3729)
at android.view.View.restoreHierarchyState(View.java:17620)
at com.android.internal.policy.PhoneWindow.restoreHierarchyState(PhoneWindow.java:2130)
at android.app.Activity.onRestoreInstanceState(Activity.java:1122)
at android.app.Activity.performRestoreInstanceState(Activity.java:1077)
I believe this is somehow tied to the fact that my nav_graph uses one of the fragments managed by the ViewPager2 as its start destination. However, if I try and use the MainFragment as my start destination, then I can't navigate to FragmentX and FragmentY, because if I do, I'll get an error saying the action isn't valid when I'm in MainFragment.
How can I use both ViewPager2 and NavGraph correctly?

Related

Android ViewPager 2 - Fragment no longer exists for key

I have an app in which a main fragment contains a tab layout tied to a ViewPager2 in order to navigate between three different fragments. When I rotate my phone's screen, the app crashes with this error:
Fragment no longer exists for key f#0: unique id c4576c9c-4bbd-4dc0-a304-b86653ed2820
(complete error stack at the end of the post)
This is my setup:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:id="#+id/main_activity_coordinator"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
app:layout_constraintTop_toTopOf="parent"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:id="#+id/app_bar"
android:theme="#style/Theme.VirtualGymBuddy.AppBarOverlay"
>
<com.google.android.material.appbar.MaterialToolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:title="#string/app_name" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.fragment.app.FragmentContainerView
android:tag="MAIN-FRAG"
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/nav_graph"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
main_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:tag="MAIN-FRAG"
android:id="#+id/main_relative_layout"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:paddingTop="?attr/actionBarSize"
xmlns:tools="http://schemas.android.com/tools">
<com.google.android.material.tabs.TabLayout
android:id="#+id/tabBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabTextAppearance="#style/MyTabLayoutStyle"
>
<com.google.android.material.tabs.TabItem
android:id="#+id/tab1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/tab1" />
<com.google.android.material.tabs.TabItem
android:id="#+id/tab2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/tab2" />
<com.google.android.material.tabs.TabItem
android:id="#+id/tab3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/tab3" />
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager2.widget.ViewPager2
android:layout_below="#id/tabBar"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
/>
</RelativeLayout>
content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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"
android:id="#+id/content_main_constraint"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<fragment
android:id="#+id/nav_host_fragment_content_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
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>
ActivityMain.java
public class MainActivity extends AppCompatActivity {
private AppBarConfiguration appBarConfiguration;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public boolean onSupportNavigateUp() {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
return NavigationUI.navigateUp(navController, appBarConfiguration)
|| super.onSupportNavigateUp();
}
}
MainFragment.java
public class MainFragment extends Fragment {
private ViewPager2 viewPager;
private NavigationAdapter navigationAdapter;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().setContentView(R.layout.main_fragment);
TabLayout tabLayout = getActivity().findViewById(R.id.tabBar);
int[] tabTexts = {
R.string.tab_1,
R.string.tab_2,
R.string.tab_3
};
// set up view pager and attach mediator to tab layout
this.viewPager = getActivity().findViewById(R.id.pager);
this.navigationAdapter = new NavigationAdapter(this);
viewPager.setAdapter(this.navigationAdapter);
new TabLayoutMediator(tabLayout, viewPager,
(tab, position) -> tab.setText(
tabTexts[position]
)
).attach();
}
}
NavigationAdapter.java
public class NavigationAdapter extends FragmentStateAdapter {
public NavigationAdapter(#NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
}
public NavigationAdapter(#NonNull Fragment fragment) {
super(fragment);
}
public NavigationAdapter(#NonNull FragmentManager fragmentManager, #NonNull Lifecycle lifecycle) {
super(fragmentManager, lifecycle);
}
#NonNull
#Override
public Fragment createFragment(int position) {
switch (position) {
case 0:
return new Fragment1();
case 1:
return new Fragment2();
case 2:
return new Fragment3();
}
throw new AssertionError();
}
#Override
public int getItemCount() {
return 3;
}
}
nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<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/nav_graph"
app:startDestination="#id/MainFragment">
<fragment
android:id="#+id/MainFragment"
android:name="com.myapp.fragments.MainFragment"
android:label="#string/first_fragment_label"
tools:layout="#layout/main_fragment" />
</navigation>
Everything works fine and I can navigate through the fragments handled by ViewPager2.
However, as soon as I rotate the screen inside of one of those fragments, I get this:
2022-05-08 00:08:12.339 12343-12343/com.myapp E/FragmentManager: Fragment no longer exists for key f#0: unique id c4576c9c-4bbd-4dc0-a304-b86653ed2820
2022-05-08 00:08:12.339 12343-12343/com.myapp E/FragmentManager: Activity state:
2022-05-08 00:08:12.358 12343-12343/com.myapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.myapp, PID: 12343
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.myapp/com.myapp.MainActivity}: java.lang.IllegalStateException: Fragment no longer exists for key f#0: unique id c4576c9c-4bbd-4dc0-a304-b86653ed2820
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2827)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2902)
at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4784)
at android.app.ActivityThread.-wrap18(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1609)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:169)
at android.app.ActivityThread.main(ActivityThread.java:6578)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: java.lang.IllegalStateException: Fragment no longer exists for key f#0: unique id c4576c9c-4bbd-4dc0-a304-b86653ed2820
at androidx.fragment.app.FragmentManager.getFragment(FragmentManager.java:975)
at androidx.viewpager2.adapter.FragmentStateAdapter.restoreState(FragmentStateAdapter.java:549)
at androidx.viewpager2.widget.ViewPager2.restorePendingState(ViewPager2.java:350)
at androidx.viewpager2.widget.ViewPager2.dispatchRestoreInstanceState(ViewPager2.java:375)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3729)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3729)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3729)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3729)
at android.view.View.restoreHierarchyState(View.java:17620)
at com.android.internal.policy.PhoneWindow.restoreHierarchyState(PhoneWindow.java:2130)
at android.app.Activity.onRestoreInstanceState(Activity.java:1122)
at android.app.Activity.performRestoreInstanceState(Activity.java:1077)
at android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.java:1260)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2800)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2902) 
at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4784) 
at android.app.ActivityThread.-wrap18(Unknown Source:0) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1609) 
at android.os.Handler.dispatchMessage(Handler.java:105) 
at android.os.Looper.loop(Looper.java:169) 
at android.app.ActivityThread.main(ActivityThread.java:6578) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767) 
What's wrong with my setup?
There is an issue with viewpager2 detach from recyclerview
https://issuetracker.google.com/issues/154751401
As per my knowledge two fixes are there:
add this line in your xml viewpager2 component.
<androidx.viewpager2.widget.ViewPager2
android:layout_below="#id/tabBar"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:saveEnabled="false">
But the above one don't save the current instance, which the fragment will recreate each time it pops back.
The recommended solution is add the below line on your mainfragment
override fun onDestroyView() {
super.onDestroyView()
viewPager.adapter = null
}

How to put custom toolbar inside fragment?

I created custom toolbar inside fragment,but i got view like this:
In themes.xml i disabled ActionBar:
<style name="Theme.Project" parent="Theme.MaterialComponents.DayNight.NoActionBar">
This is my custom toolbar layout:
<?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"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/white"
>
<TextView
android:id="#+id/toolbar_title"
style="#style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Home"
android:fontFamily="#font/google_sans"
android:textColor="#color/black"
android:textSize="20sp" />
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<include layout="#layout/fragment_home" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
And this is HomeFragment.java:
public class HomeFragment extends Fragment {
private HomeViewModel homeViewModel;
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
homeViewModel =
new ViewModelProvider(this).get(HomeViewModel.class);
View root = inflater.inflate(R.layout.toolbar_in_home, container, false);
return root;
}
}
How to remove this white thing on top of screen?
UPD:
<?xml version="1.0" encoding="utf-8"?>
<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=".ui.home.HomeFragment">
</androidx.constraintlayout.widget.ConstraintLayout>
This is fragment home(just ConstraintLayout)
UPD2:i also was trying use toolbar in activity_main and it works:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.toolbar);
BottomNavigationView navView = findViewById(R.id.nav_view);
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
R.id.navigation_home, R.id.navigation_examination, R.id.navigation_profile)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupWithNavController(navView, navController);
}
But i can't do changes if i want to go on other fragment.
I need to set another custom toolbar layout with button like this:
So this is how I did it. My app is based on single activity-multi-fragment model.
I had set activity theme to AppTheme.NoActionBar, so it enabled me to set a custom toolbar.
Later for each fragment I had a separate layout and within which added a custom toolbar like below
<androidx.appcompat.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/colorPrimary">
....
....
</androidx.appcompat.widget.Toolbar>
Within this layout I had added menu items.
If you don't want to follow this, may be you should look at getActionBar().setCustomView(). Keep your activity theme set, so getActionBar() is not null.
You can control action bar layout by calling getActionBar().setCustomView() in fragment onResume() or probably when you are adding or removing a fragment in your activity.
You can use your custom toolbar inside the main layout replace it from com.google.android.material.appbar.AppBarLayout.
Here is an example code:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ads="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="#+id/header"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_alignParentTop="true"
android:layout_centerInParent="true"
android:background="#color/colorPrimary">
<ImageView
android:id="#+id/navi"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentStart="true"
android:layout_centerInParent="true"
android:layout_marginStart="20dp"
android:contentDescription="#string/todo"
android:src="#drawable/navi" />
<ImageView
android:layout_width="180dp"
android:layout_height="?attr/actionBarSize"
android:layout_alignParentTop="true"
android:layout_centerInParent="true"
android:contentDescription="#string/todo"
android:src="#drawable/header2" />
</RelativeLayout>
<include layout="#layout/fragment_home" />
</RelativeLayout>

Android Navigation Component pass param to drawer nav entry

I'm using the android navigation component with a drawer menu. My toolbar contains a spinner element. I've have only access to the spinner-element on MainActivity.java. Now I would like to pass the user selection of the spinner to a drawermenu item.
NavController (in MainActivity.java)
// set up navigation
navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupWithNavController(binding.navView, navController);
// top level dest
Set<Integer> topLevelDest = new HashSet<>();
topLevelDest.add(R.id.nav_foo);
topLevelDest.add(R.id.nav_bar);
appBarConfiguration =
new AppBarConfiguration.Builder(topLevelDest).setDrawerLayout(binding.drawerLayout)
.build();
NavigationUI.setupWithNavController(binding.toolbar.toolbar, navController, appBarConfiguration);
Here is my MainActivity.xml which includes a Drawerlayout, NavHostFragment, and CustomToolbar:
<androidx.drawerlayout.widget.DrawerLayout
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="#+id/toolbar"
layout="#layout/toolbar" />
<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/drawer_navigation" />
</LinearLayout>
<com.google.android.material.navigation.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:menu="#menu/drawer_menu" />
</androidx.drawerlayout.widget.DrawerLayout>
And here is my Navigationgraph:
<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/drawer_navigation"
app:startDestination="#id/nav_foo">
<fragment
android:id="#id/nav_foo"
android:name="FooFragment"
tools:layout="#layout/fragment_foo"/>
<fragment
android:id="#id/nav_bar"
android:name="BarFragment">
</fragment>
<fragment
android:id="#+id/nav_A"
android:name="AFragment"
tools:layout="#layout/fragment_A" />
</navigation>
I can acces the spinner in MainActivity via binding.toolbar.spinner
Now I would like to pass the selection of the spinner (int) in the toolbar to the fragment_A, which is also a DrawerNav-Entry but no toplevel destination.
Is this even possible?
I temporarly solved it over shared-preferences
Regards

Fragment not showing in home activity

I added navigation drawer on map activity. Now I'm trying to add a fragment on my home activity, but fragment is not showing in activity, but code in fragment class is working.
Is my FrameLayout hiding behind the map fragment?
I'm very new to fragments.
Please help me.
Home class layout
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="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:id="#+id/constrain_layout"
android:orientation="vertical">
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
map:layout_constraintBottom_toBottomOf="parent"
map:layout_constraintEnd_toEndOf="parent"
map:layout_constraintStart_toStartOf="parent"
map:layout_constraintTop_toTopOf="parent" />
<fragment
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:layout_constraintBottom_toBottomOf="parent"
map:layout_constraintEnd_toEndOf="parent"
map:layout_constraintHorizontal_bias="0.0"
map:layout_constraintStart_toStartOf="parent"
map:layout_constraintTop_toTopOf="parent"
map:layout_constraintVertical_bias="0.0"
tools:context=".HomeActivity" />
</androidx.constraintlayout.widget.ConstraintLayout>
Fragment class
public class RideHistory extends Fragment {
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
Toast.makeText(getActivity(), "Ride History", Toast.LENGTH_SHORT).show();
View view = inflater.inflate(R.layout.fragment_ride_history,container,false);
return view;
}
}
Fragment xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Fragments.RideHistory">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Ride History" />
</FrameLayout>
Change layout constraints so that both widgets frame layout and map fragment do not overlap eachother.
for ex:
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
map:layout_constraintBottom_toTopOf="#+id/map"
map:layout_constraintEnd_toEndOf="parent"
map:layout_constraintStart_toStartOf="parent"
map:layout_constraintTop_toTopOf="parent" />
<fragment
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="434dp"
android:layout_height="297dp"
map:layout_constraintBottom_toBottomOf="parent"
map:layout_constraintEnd_toEndOf="parent"
map:layout_constraintHorizontal_bias="0.0"
map:layout_constraintStart_toStartOf="parent"
tools:context=".HomeActivity" />

Can't find any java examples of using the androidx navigation architecture for single activity using bottom navigation

I'm unable to find any examples using java, all the codelabs given by google seem to be in kotlin and ideally i don't want to learn that language.
Is there any simple example of using 1 activity, 2/3 fragments all linked in by the bottom navigation bar that I can browse for?
I've looked at all the documentation provided by google and unable to make any sense of it.
All I do know is that i am expected to use
NavHostFragment.findNavController(Fragment)
Navigation.findNavController(Activity, #IdRes int viewId)
Navigation.findNavController(View)
And that in java i should be using
Navigation.createNavigateOnClickListener()
I am hoping i am not too far off with this:
public class MainActivity extends AppCompatActivity {
NavController navController;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BottomNavigationView navView = findViewById(R.id.nav_view);
navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupWithNavController(navView,navController);
}
}
Thanks.
Eventually found the answer, for anyone else who is facing similar newbie issues:
/res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<fragment
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="#navigation/nav_graph"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/nav_view"/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/bottom_nav_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.java under onCreate
BottomNavigationView navView = findViewById(R.id.nav_view);
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupWithNavController(navView,navController);
Under /res/navigation/nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<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/nav_graph"
app:startDestination="#id/mapFragment">
<fragment
android:id="#+id/mapFragment"
android:name="com.company.pinpoint.MapFragment"
android:label="fragment_map"
tools:layout="#layout/fragment_map">
<action
android:id="#+id/action_mapFragment_to_newsFeedFragment"
app:destination="#id/newsFeedFragment" />
<!--app:enterAnim="#anim/slide_in_right"-->
<!--app:exitAnim="#anim/slide_out_left"-->
<!--app:popEnterAnim="#anim/slide_in_left"-->
<!--app:popExitAnim="#anim/slide_out_right" />-->
</fragment>
<fragment
android:id="#+id/newsFeedFragment"
android:name="com.company.pinpoint.NewsFeedFragment"
android:label="fragment_news_feed"
tools:layout="#layout/fragment_news_feed" />
</navigation>

Categories

Resources