I have my MainActivity setup which looks like this :
<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.host.HostActivity">
<fragment
android:id="#+id/mainNavigationFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
app:labelVisibilityMode="labeled"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="#menu/main_nav_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>
And the bottom navigation has 3 tabs which can only be shown after some initial authentication and setup user calls have been performed.
What is the best way to show a Loading/Setting up user screen before I let user start using the bottom nav view.
One way I am thinking is to delay the setup of NaHostFragment in my Activity until all the setup is done.
So basically toggle the visibility of mainNavigationFragment and BottomNavigationView till the setup is completed.
Are there any other ideas ?
The NavigationUI documentation actually uses hiding a BottomNavigationView as its example. You could also use an OnDestinationChangedListener to update the visibility of your BottomNavigationView, say, only hiding it while the user is on the login screen (note that as per the Conditional Navigation documentation, you shouldn't be doing this in the start destination of your graph, but redirecting users to those screens):
navController.addOnDestinationChangedListener { _, destination, _ ->
if(destination.parent?.id == R.id.login) {
bottomNavigationView.visibility = View.GONE
} else {
bottomNavigationView.visibility = View.VISIBLE
}
}
in my Activity
//using view model
loginViewModel.isLoggedIn.observe(this, Observer { loggedIn ->
if(!loggedIn) {
startActivity(Intent(this#MainActivity,LoginActivity::class.java))
finish()
}
else {
//set up the controller
val navController = findNavController(R.id.nav_host_fragment)
nav_view.setupWithNavController(navController)
navController.addOnDestinationChangedListener { controller, destination, arguments ->
when(destination.id) {
R.id.navigation_home,R.id.navigation_profile,R.id.navigation_account -> {
nav_view.visibility = View.VISIBLE
}
else -> {
nav_view.visibility = View.GONE
}
}
}
}
Related
when I switch between the fragment I created, the home fragment is not deleted, it writes on the others.
please i have an issue. I am using bottom navigation and nav controller. once I switch fragments, the home fragment keeps showing under the rest
`
class FeedActivity : AppCompatActivity() {
private lateinit var bottomNavigationView: BottomNavigationView
//loadFragment(HomeFragment())
bottomNavigationView = findViewById(R.id.bottom_navigation) as BottomNavigationView
bottomNavigationView.setOnItemSelectedListener {
when (it.itemId) {
R.id.nav_home -> {
loadFragment(HomeFragment())
true
}
R.id.nav_search -> {
loadFragment(SearchFragment())
true
}
R.id.nav_add -> {
loadFragment(AddFragment())
true
}
R.id.nav_notifications -> {
loadFragment(NotificationsFragment())
true
}
R.id.nav_profile -> {
loadFragment(ProfileFragment())
true
}
else -> throw IllegalStateException("Someone forgot to add enough fragment cases to 'when' clause!")
}
}
}
private fun loadFragment(fragment: Fragment){
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.cercevekapsayici,fragment)
transaction.commit()
}`
`<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"
tools:context=".view.FeedActivity">
<FrameLayout
android:id="#+id/cercevekapsayici"
android:layout_above="#id/bottom_appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/bottom_appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" >
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
app:itemIconTint="#color/cardview_dark_background"
app:labelVisibilityMode="unlabeled"
app:menu="#menu/bottom_navigation" />
</com.google.android.material.appbar.AppBarLayout>
[[enter image description here](https://i.stack.imgur.com/3D2Z8.png)](https://i.stack.imgur.com/llwZ5.png)`
I want the recyclerview to disappear when I switch to other fragments.
You can make your FramLayout height match parent :
<FrameLayout
android:id="#+id/cercevekapsayici"
android:layout_above="#id/bottom_appbar"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Your loadFragment function replaces whatever Fragment is attached to R.id.cercevekapsayici, which is a FrameLayout that contains your fragments. But your RecyclerView is part of your main layout, so it will always be there whatever you attach to R.id.cercevekapsayici.
If you want it to be replaced, put it in a Fragment and add it to that FrameLayout - then it'll be removed when you switch to a different fragment.
Maybe you need to give background to the other fragments
everyone. I have a BottomnNavigationsBar and a FrameLayout inside my activity_main.xml file. In the frame I display a ViewPager within a Fragment. The navigation contains 3 elements. If I click on one, the clicked status does not change. The respective fragment is displayed correctly, but the icon does not change in color and size.
What am I doing wrong or what do I have to change?
Here is my MainActivity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(viewBinding.root)
setFragementTo(HomeFragment.newInstance("",""))
viewBinding.bottomNavigationView.setOnItemSelectedListener{
when(it.itemId)
{
R.id.home ->{
setFragementTo(HomeFragment.newInstance("",""))
}
R.id.neu ->{
setFragementTo(NewFragment.newInstance("",""))
}
R.id.settings ->{
setFragementTo(FilterFragment.newInstance("",""))
}
}
false
}
}
private fun setFragementTo(fragment: Fragment) {
supportFragmentManager.beginTransaction().replace(R.id.myFrameInMain,fragment).commit()
}
activity_main.xml:
<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=".MainActivity">
<FrameLayout
android:id="#+id/myFrameInMain"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/bottomNavigationView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"></FrameLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:background="#drawable/bottom_nav_style"
app:itemRippleColor="#color/white"
app:itemTextColor="#color/item_bottom_nav"
app:itemIconTint="#color/item_bottom_nav"
android:id="#+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="#menu/bottom_nav"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Here I click on the middle icon in the navigation. The fragment appears correctly, but the icon in the navigation does not
And here the filter icon:
I think I got it. I had to change the parameter from false to true in the setOnItemSelectedListener method
I wanted my app to change fragment view and the selected item in bottom navigation menu. I tried doing this:
private void replaceFragment(Fragment fragment){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frameLayout,fragment);
fragmentTransaction.commit();
}
It changes the fragment but the item selected in the bottom navigation menu doesn't.
I tried doing startActivity(new Intent(activity.this, destination.class)); but it doesn't suit the app because it also refreshes the bottom nav menu which is not what I wanted. What I wanted is the fragment and the selected item in bottom navigation menu to both change.
this is the fragment where the button is
I want the app to go here when the book now button is clicked:
this is where i wanted it to go
As you can see in this picture, the fragment changed but the item selected does not. This is when I did the replaceFragment code when i use the replaceFragment code
You can use Bottom Navigation view implementation from Jet pack Navigation component for this work. it is easy and clear. Bottom navigation will handle all item selected use cases by default. Also you can customized it easily.
Here I have added Kotlin example for bottom navigation setup.
Step 01:
add the following dependencies to your app's build.gradle file:
dependencies {
val nav_version = "2.4.2"
// Java language implementation
implementation("androidx.navigation:navigation-fragment:$nav_version")
implementation("androidx.navigation:navigation-ui:$nav_version")
// Kotlin
implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
implementation("androidx.navigation:navigation-ui-ktx:$nav_version")
}
Step 02: Add a NavHostFragment via XML as part of an app's main activity:
<?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=".MainActivity">
<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_navigation_menu" />
<fragment
android:id="#+id/nav_host_fragment_activity_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="#id/nav_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/main_nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
Step 03: init Bottom navigation view and navigation controller from your main main activity OnCreate()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(LayoutInflater.from(this))
setContentView(binding.root)
val navView : BottomNavigationView = binding.navView
val navController = findNavController(R.id.nav_host_fragment_activity_main)
navView.setupWithNavController(navController)
// Get bottom nav listener
navController.addOnDestinationChangedListener
{ _, destination, _ ->
when (destination.id) {
R.id.A_Fragment -> {
// Add your requirement here base with destination
}
else -> {
// Add your requirement here
}
}
// Manage bottom nav visibility and etc
if (destination.id in arrayOf(
R.id.A_Fragment,
R.id.B_Fragment,
R.id.C_Fragment
)) {
navView.visibility = View.VISIBLE
this.supportActionBar?.show()
setupToolbar()
}
else {
navView.visibility = View.GONE
this.supportActionBar?.hide()
// No Toolbar
}
}
}
Also you can define destination with navigation graph with this implementation.
If you need to add or customized bottomNaivationView item badge, you can use following example fun:
private fun setupBadge(bottomNavigationView: BottomNavigationView) {
val menu: Menu = bottomNavigationView.menu
val your_A_MenuItem: MenuItem = menu.getItem(1)
val your_B_MenuItem: MenuItem = menu.getItem(2)
val your_A_Badge = bottomNavigationView.getOrCreateBadge(your_A_MenuItem.itemId)
val your_B_Badge = bottomNavigationView.getOrCreateBadge(your_B_MenuItem.itemId)
// Add numbers
your_A_Badge.number = 10
your_A_Badge.isVisible = true
}
I have successfully integrated the bottom navigation with the latest android architecture navigation components. The following is my complete code.
Navigation
<?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/mobile_navigation"
app:startDestination="#+id/navigation_home">
<fragment
android:id="#+id/navigation_home"
android:name="in.zedone.bottomsample.ui.home.HomeFragment"
android:label="#string/title_home"
tools:layout="#layout/fragment_home" />
<fragment
android:id="#+id/navigation_saloons"
android:name="in.zedone.bottomsample.ui.saloons.SaloonsFragment"
android:label="#string/title_saloon"
tools:layout="#layout/fragment_saloons" />
<fragment
android:id="#+id/navigation_offers"
android:name="in.zedone.bottomsample.ui.offers.OffersFragment"
android:label="#string/title_offer"
tools:layout="#layout/fragment_offers" />
<fragment
android:id="#+id/navigation_account"
android:name="in.zedone.bottomsample.ui.account.AccountFragment"
android:label="#string/title_account"
tools:layout="#layout/fragment_account" />
</navigation>
BottomNavigationView
<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:labelVisibilityMode="labeled"
app:itemTextAppearanceActive="#style/BottomNavigationView.Active"
app:itemTextAppearanceInactive="#style/BottomNavigationView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/bottom_nav_menu" />
MainActivity
BottomNavigationView navView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
R.id.navigation_home, R.id.navigation_saloons, R.id.navigation_offers,R.id.navigation_account)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(navView, navController);
Now how can I add transition/animation on select each tab/fragment in bottom navigation?
Instead of using setupWithNavController function, follow this way.
First, create your NavOptions which include animation shown below.
val options = NavOptions.Builder()
.setLaunchSingleTop(true)
.setEnterAnim(R.anim.enter_from_bottom)
.setExitAnim(R.anim.exit_to_top)
.setPopEnterAnim(R.anim.enter_from_top)
.setPopExitAnim(R.anim.exit_to_bottom)
.setPopUpTo(navController.graph.startDestination, false)
.build()
Then use setOnNavigationItemSelectedListener to navigate with animation like that.
bottomNavigationView.setOnNavigationItemSelectedListener { item ->
when(item.itemId) {
R.id.fragmentFirst -> {
navController.navigate(R.id.fragmentFirst,null,options)
}
R.id.fragmentSecond -> {
navController.navigate(R.id.fragmentSecond,null,options)
}
R.id.fragmentThird -> {
navController.navigate(R.id.fragmentThird,null,options)
}
}
true
}
Finally, you should prevent same item selection case so you can add below code.
bottomNavigationView.setOnNavigationItemReselectedListener { item ->
return#setOnNavigationItemReselectedListener
I used bottomNavigation like that in my project to add animation for page transitions.
I hope it helped.
its work with BottomNavigationView's siblings fragment also(JetPack Navigation Components)
// FragmentA.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
exitTransition = MaterialFadeThrough()
}
// FragmentB.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialFadeThrough()
}
You can also do it with keeping setupWithNavController by overriding onCreateAnimation() in each Fragment and checking whether or not you're entering or exiting the Fragment with the enter parameter and then creating the appropriate Animation with AnimationUtils.loadAnimation(context, animationId).
override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {
return if (enter) {
AnimationUtils.loadAnimation(context, R.anim.fade_in)
} else {
AnimationUtils.loadAnimation(context, R.anim.fade_out)
}
}
EDIT as a response to iloo:
According to Material Design (https://material.io/components/bottom-navigation#behavior) you shouldn't be doing that in the first place, but I guess it's still achievable.
Since when using setupWithNavController all destinations are top level there will
ever be only one previousBackStackEntry whose destination points to the home
destination, therefore previousBackStackEntry is not much of a help in figuring out which Fragment are you coming from.
Other approach could be to have a public variable in the Activity, where you will store the
last destination you were in and set that variable in each Fragment upon resuming.
And you can check that variable in onAnimationCreate to know which Fragment you're coming from
and apply an appropriate animation.
I'm kinda new to the Android Navigation but this is what I think you can try if you like to change the default animation
put those file in your anim directory
nav_default_enter_anim.xml
nav_default_exit_anim.xml
nav_default_pop_enter_anim.xml
nav_default_pop_exit_anim.xml
and the default animation for the transactions will change to the animation you put in the above files.
I want to navigate between activities while coloring the current item on bottom navigation.
What is the best approach to achieve this?
Currently, on every activity when I'm using BottomNavigationView (in this casecom.google.android.material.bottomnavigation.BottomNavigationView),
I'm duplicating almost the same code for navigating between activities which is hard to maintain:
Marking the current itemId
Setting thesetOnNavigationItemSelectedListener without the current itemId.
jesta_bottom_navigation.selectedItemId = R.id.nav_do_jesta
jesta_bottom_navigation.setOnNavigationItemSelectedListener {
val intent = when (it.itemId) {
R.id.nav_ask_jesta -> {
Intent(this#DoJestaActivity, AskJestaActivity::class.java)
}
R.id.nav_status -> {
Intent(this#DoJestaActivity, StatusActivity::class.java)
}
// Settings Activity
else -> {
Intent(this#DoJestaActivity, SettingsActivity::class.java)
}
}
startActivity(intent)
true
}
frame_bottom_navigation_view.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/jesta_bottom_navigation"
style="#style/Widget.Jesta.BottomNavigationView"
android:layout_width="match_parent"
android:layout_height="#dimen/bottom_bar"
android:layout_gravity="bottom"
app:itemIconSize="30dp"
app:itemIconTint="#drawable/jesta_bottom_navigation_colors"
app:labelVisibilityMode="unlabeled"
app:menu="#menu/bottom_nav_drawer_menu" />
</FrameLayout>
What about using an external library for doing this faster
AHBottomNavigation
BottomNavigationViewEx