I've used the default bottom navigation template in Android Studio. This has been working fine so far. However, I'd like to change one of the buttons to a button which triggers a camera intent. I'm not sure how to do this since I assume the default implementation uses built-in functions which I can't change.
I believe setupWithNavController is responsible for navigating to the matching fragment. Is there any way to change this, so that I add a button that launches the camera?
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
navView.setupWithNavController(navController)
}
}
Remove setupWithNavController and create your own menu.xml file. Then you can use your menu with adding like this:
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottomNavigationBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="#menu/main" />
After that, you can implement setOnNavigationItemSelectedListener on your NavigationBar inside Kotlin class.
Related
List the item
I'm developing an app using a navigation drawer and navigation components and I'm facing two issues:
I settled specifically each toolbar title where it is supposed to be, but every time I change the fragments, in the toolbar, for an instant, I can see the previous name from the fragment, which is the fragment name itself. So, it quickly changes from MySpecificFragment to MyFragmentName and I would like it to not happen. I've settled the title even onCreateView or onViewCreated. It didn't matter, still happening.
How could I decide the direction in which the back button of the fragment goes? I would like to create a standard position where the back button arrow goes, always the same. But it just travels back to the previous fragment (which is not a real problem, but I would like to improve its behavior)
Sorry for the lack of code, I don't know what I am supposed to display since I'm going against the standard android behavior.
P.S.: Using android studio and kotlin
Regarding the first issue, one way to avoid the brief display of the previous fragment name in the toolbar is to set the toolbar title in the parent activity and then update it from the fragment's onResume() method. This ensures that the toolbar title is set correctly when the fragment is resumed after being pushed onto the back stack. Here's an example code snippet:
In your activity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
}
fun setToolbarTitle(title: String) {
supportActionBar?.title = title
}
}
In your fragment:
class MySpecificFragment : Fragment() {
override fun onResume() {
super.onResume()
(activity as? MainActivity)?.setToolbarTitle("MySpecificFragment")
}
}
Regarding the second issue, you can customize the back button behavior by using a custom NavController.OnDestinationChangedListener. In the listener, you can set the back button icon and its behavior based on the current and previous destinations. Here's an example code snippet:
class MyNavigationController(activity: AppCompatActivity, navController: NavController) {
init {
navController.addOnDestinationChangedListener(
activity, object : NavController.OnDestinationChangedListener {
override fun onDestinationChanged(
controller: NavController,
destination: NavDestination,
arguments: Bundle?
) {
if (destination.id == R.id.my_fragment) {
activity.supportActionBar?.setDisplayHomeAsUpEnabled(false)
} else {
activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back)
activity.supportActionBar?.setHomeActionContentDescription(R.string.back)
activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back)
activity.supportActionBar?.setDisplayShowHomeEnabled(true)
}
}
})
}
}
Here, you can adjust the back button icon and behavior based on the current and previous destinations by setting setDisplayHomeAsUpEnabled, setHomeAsUpIndicator, and setHomeActionContentDescription.
I’m curious about an issue I’m having with Navigation Component, specifically on hosting NavHostFragment. I have a single-Activity native app with a few Fragments. I’m in the launcher’s xml, replacing the <fragment> with <androidx.fragment.app.FragmentContainerView>.
The initial implementation has worked as intended for a year. While introducing the FragmentContainerView this week the starting destination’s screen doesn’t render at launch. Yet I observe logs that prove successful data incoming from network and cache. From this state, only triggering configuration change by rotating the device renders UI as expected.
My AndroidX NavigationFragmentKtx and NavigationUiKtx dependencies are version 2.3.2. Relevant files abbreviated. Does anything stand out that I've missed? Any thoughts are appreciated.
launcher_activity.xml
<androidx.fragment.app.FragmentContainerView
android:id="#+id/launcher_navHost"
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_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/nav_graph" />
LauncherActivity.kt
class LauncherActivity : AppCompatActivity(R.layout.launcher_activity) {
private lateinit var viewBinding: LauncherActivityBinding
private val navController: NavController by lazy {
val navHost = supportFragmentManager.findFragmentById(
R.id.launcher_navHost
) as NavHostFragment
navHost.navController
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewBinding = LauncherActivityBinding.inflate(layoutInflater)
setContentView(viewBinding.root)
}
}
You're using AppCompatActivity(R.layout.launcher_activity), which means you are automatically calling setContentView(R.layout.launcher_activity) as part of super.onCreate(savedInstanceState) (that's what passing a layout ID does). You're then overriding that layout with a second call to setContentView().
You can just remove the R.layout.launcher_activity part if you're using View Binding as it is not needed (and the root of your issue):
class LauncherActivity : AppCompatActivity() {
I am currently working on an app that requires the navigation template from creation. I have been fine-tuning the look of the default navigation template for the app I need to work on. However, there's one thing I have been unable to find a solution to which is to change the text in the app bar. It keeps saying 'Home' but I need to put the title of my app there. I have tried several things such as: setTitle("App title here") in the mainActivity, and in the manifest file I have also tried changing the text in android:label to the App title but nothing I do from the solutions I find online seems to work.
Is there any way I can get around this and change the 'home' text into my App's title? Thanks in advance for any suggestions.
Code inside OnCreate as requested in a comment:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setTitle("Simple Dark Calculator")
setContentView(R.layout.activity_main)
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(setOf(
R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow), drawerLayout)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
Check the doc:
NavigationUI uses the destination labels from your navigation graph to keep the title of the top app bar up-to-date.
In your navigation graph change the android:label of the startDestination.
<navigation
app:startDestination="#+id/nav_home">
<fragment
android:id="#+id/nav_home"
android:label="#string/menu_home"
... >
</fragment>
You can also use the OnDestinationChangedListener to set the title after your setup method:
navController.addOnDestinationChangedListener { controller, destination, arguments ->
if (destination.id == R.id.nav_xxx){
supportActionBar?.title = "My Title"
}
//.....
}
Try to remove setTitle("Simple Dark Calculator") line and add code below after setSupportActionBar method:
getSupportActionBar().setTitle("Simple Dark Calculator");
To read more about setting up the app bar:
Set up the app bar
Try this in Manifest:
<activity android:name=".Youractivityname"
android:label="#string/app_name"
android:theme="#style/AppTheme.NoActionBar"
android:windowSoftInputMode="adjustResize"/>
What I'm trying to do
I am using Android Navigation component to handle navigation in my app. In this example, I have two screens, screen A and screen B.
I want the user to be able to click a button in screen A and be able to navigate to screen B; and then be prevented from going back to the previous screen (screen A).
The problem
When the user navigates to screen B from screen A, the back button on the action bar still allows the user to go back to the previous screen, however when clicking on the back button in the bottom bar it exits the app so this part works OK.
What do I need to do in order to remove the back button in the Action Bar?
What I've read so far
I have followed the guidance within these three articles but I think they might be ignoring the ActionBar's back button:
Stackoverflow - How to clear navigation Stack after navigating to
another fragment in Android
Android Developer Guide - Conditional navigation
Android Developer Guide - Navigate to a destination
My Code
Navigation Graph - 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"
android:id="#+id/nav_graph"
app:startDestination="#id/screen_a">
<fragment
android:id="#+id/screen_a"
android:name="com.example.conditionalnavigation.AFragment"
android:label="screen A">
<action
android:id="#+id/action_AFragment_to_BFragment"
app:destination="#id/screen_b"
app:launchSingleTop="true"
app:popUpTo="#id/screen_a"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/screen_b"
android:name="com.example.conditionalnavigation.BFragment"
android:label="screen B" />
</navigation>
MainActivity - This acts as my Single Activity navhost.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val navController = this.findNavController(R.id.myNavHostFragment)
NavigationUI.setupActionBarWithNavController(this, navController)
}
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(R.id.myNavHostFragment)
return navController.navigateUp()
}
}
In your activity class add the following member (in Kotlin):
private lateinit var appBarConfiguration: AppBarConfiguration
Inside the onCreate method add the following lines:
....
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
...
...
appBarConfiguration = AppBarConfiguration(
setOf([**ID of the fragment layout you want without back button**],
), drawerLayout
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
....
In this way your fragment will be a root fragment and the back button is removed. Hope it helps.
Try to disable home button at the creation of screen b fragment:
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
var rootView = inflater?.inflate(R.layout.fragment_screen_b, container, false)
(activity as AppCompatActivity).supportActionBar!!.setDisplayHomeAsUpEnabled(false)
return rootView
}
If it didn't work, then try it in onViewCreated() method.
If not worked, try to add below as well:
setHasOptionsMenu(false)
Is it really necessary to call
override fun onSupportNavigateUp() = findNavController(R.id.nav_host_fragment).navigateUp()
in my MainActivity ?
Because when I attach the NavHostFragment in my xml it starts from there, but I have seen the Google I/O lecture where they use it to make the start point of the navigation
My question
Where is it necessary to use it ?
Thanks
onSupportNavigateUp comes from AppCompatActivity. You should override the method in the same activity where you define your NavHostFragment (probably your MainActivity). You override it so that the NavigationUI can correctly support the up navigation or even the drawer layout menu. AppCompatActivity and NavigationUI are two indepenent components, so you override the method in order to connect the two. Note that if you set a toolbar with the navigation component, i.e., if you do something similar to
override fun onCreate(savedInstanceState: Bundle?) {
setContentView(R.layout.activity_main)
// ...
val navController = findNavController(R.id.nav_host_fragment)
val appBarConfiguration = AppBarConfiguration(navController.graph)
findViewById<Toolbar>(R.id.toolbar).setupWithNavController(navController, appBarConfiguration)
}
you don't need to override the onSupportNavigationUp method as Navigation will automatically handle the click events.
You also don't need to override it if you want to handle the toolbar yourself.