I have a form and want to get a confirmation message from the user before the user leaves it.
i want provide custom back button when user touch this button:
i try this:
val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
}
}
requireActivity().onBackPressedDispatcher.addCallback(this,onBackPressedCallback)
but only seems to work for providing custom back behavior to the built-in software/hardware back button and not the back arrow button
How can I do this?
Use onSupportNavigateUp, and replace yourCurrentFragmentID to your current fragment id. All these should be done in MainActivity.
navController = findNavController(R.id.nav_host_fragment)
setupActionBarWithNavController(navController!!)
override fun onSupportNavigateUp(): Boolean {
return when(navController?.currentDestination?.id) {
R.id.yourCurrentFragmentID -> {
showDialog()
true
}
else -> navController?.navigateUp()!!
}
}
Edit
If your fragment already use onOptionsItemSelected, you can handle the logic by checking itemId.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
if (id == R.id.save) {
// your save button code logic
}else if(id ==android.R.id.home){
// display confirmation message
}
return super.onOptionsItemSelected(item)
}
Use this code to set Activity on backpress.
override fun onBackPressed() {
if (isDiscardChanges) {
discardDialog()
} else {
super.onBackPressed()
}
}
If you only want to go back from Activity, then you can add within AndroidManifest.xml child class Activity.
android:parentActivityName="Parent_Activity_Name
Related
I need to show a dialog when I press back from my fragment, it works for the system backbutton bar doing this
requireActivity().onBackPressedDispatcher.addCallback {
showDialog()
}
but, its not poping on navigateUp() click
I have tried
override fun onSupportNavigateUp(): Boolean {
return when(navController.currentDestination?.id){
R.id.nav_fragment1 -> {
showDialog()
false
}
else -> navController.navigateUp()
}
}
But it also does not work, any Idea how to get the two working with the same code ?
Just use this code block :
binding.toolbar.setNavigationOnClickListener {
when (navController.currentDestination?.id) {
R.id.nav_fragment1-> {
if (onBackPressedDispatcher.hasEnabledCallbacks())
onBackPressedDispatcher.onBackPressed()
else
navController.navigateUp()
}
else -> navController.navigateUp()
}
}
I am creating a collapsing toolbar with navigationview it works so will but when I use onOptionsItemSelected to make a Toast when item of menu chooses, there is no Toast.
Could anyone help me to know What is wrong?
this is my MainActivity
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
main_nav_view.setNavigationItemSelectedListener(this)
//set navigation view
val toggle = ActionBarDrawerToggle(
this, main_drawer_layout, main_toolbar, R.string.navigation_drawer_open,
R.string.navigation_drawer_close
)
main_drawer_layout.addDrawerListener(toggle)
toggle.syncState()
}
override fun onNavigationItemSelected(menuItem: MenuItem): Boolean {
// set item as selected to persist highlight
menuItem.isChecked = true
closeDrawer()
return true
}
// close drawer when item is tapped
private fun closeDrawer() {
main_drawer_layout.closeDrawer(Gravity.START)
}
override fun onBackPressed() {
if (main_drawer_layout.isDrawerOpen(GravityCompat.START)) {
closeDrawer()
} else {
super.onBackPressed()
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// Handle presses on the action bar menu items
when (item.itemId) {
R.id.item1 -> {
Toast.makeText(this, "Item 1 pressed", Toast.LENGTH_LONG).show()
return true
}
}
return super.onOptionsItemSelected(item)
}
}
The context should be "MainActivity.this" instead of "this" since it's inside of a callback method.
What happens if you set a breakpoint on the Toast line? Is the breakpoint reached?
you must create a new function because it conflicts with the navigationselected. You can create a function called initNavigationDrawer () and call it in oncreate.
Try to set breakpoint in your line where you are expecting Toast. Maybe there is some problem with logic or id of element.
How to set breakpoint:
click near line number and red dot should appear
run in Debug mode
When program stops there it means that evertyhing is correct from logic point of view.
You can also try to change this in to this#MainActivity to pass proper contex (to method Toast.makeText(...)).
I have a menu inside the top actionbar that allows for changing a password, editing their profile, and logging out. What I need is, once they click the "edit bio" option, I want to add an arrow or a "<" to the actionbar on the left side to allow the user a "go back" option if they want to exit editing the profile as well as hiding the right side menu option while they are editing. Here's my code for inflating the menu on the right:
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
inflater!!.inflate(R.menu.profiletoolbar, menu)
super.onCreateOptionsMenu(menu, inflater)
}
//get the actionbar selction when pressed
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
R.id.userLogout -> {
FirebaseAuth.getInstance().signOut()
val i = Intent(context, Login::class.java)
startActivity(i)
true
}
R.id.editBio -> {
editProfileBio()
true
}
else -> super.onOptionsItemSelected(item)
}
I really want to understand what the process is behind how this works. Can someone help me? I'd also be okay with just replacing the menu on the right with an arrow or a "<" as well, although I don't like that idea as much.
How about this: setDisplayHomeAsUpEnabled()
R.id.editBio -> {
editProfileBio()
supportActionBar?.setDisplayHomeAsUpEnabled(true)
true
}
To handle when pressed:
override fun onBackPressed() {
super.onBackPressed()
// do your stuff when pressed the back Button
}
I'd also be okay with just replacing the menu on the right with an
arrow or a "<" as well, although I don't like that idea as much.
Don't like that idea either. By enabling back Button when editbio clicked, you'll have access to the current menu and there will be a back Button too so this seems to be the perfect way to do that.
You just have to listen for the R.id.home option
Add a toolbar to activity layout.
In your fragment declare that it needs to redraw the toolbar.
setHasOptionsMenu(true);
Set toolbar as action bar
private fun setActionBar() {
val toolbar = getActivity().findViewById(R.id.tb_main_toolbar)
if (toolbar != null) {
(getActivity() as AppCompatActivity).setSupportActionBar(toolbar)
}
val actionBar = (getActivity() as AppCompatActivity).supportActionBar
if (null != actionBar) {
customizeActionBar(actionBar)
}
}
private fun customizeActionBar(actionBar: ActionBar) {
actionBar.setDisplayHomeAsUpEnabled(true)
actionBar.setTitle(R.string.logout)
actionBar.setDisplayHomeAsUpEnabled(true)
actionbar.setDisplayShowHomeEnabled(true)
actionBar.setHomeAsUpIndicator(R.drawable.logout)
}
Override onOptionItemSelected and do your functionality
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home ->
FirebaseAuth.getInstance().signOut()
val i = Intent(context, Login::class.java)
startActivity(i)
true
}
return true
}
I need to display custom AlertDialog, but only when there are no more fragments after calling NavController.navigateUp(). My current code does something similar, but there is a bug to it:
override fun onBackPressed() {
if (navController.navigateUp()) {
return
}
showQuitDialog()
}
This somewhat works, but if I cancel the AlertDialog and don't quit the app, navController already navigated up to NavGraph root, which is not the fragment in which I was when AlertDialog appeared. So, if I try to use any navigation action from that fragment, I get error:
java.lang.IllegalArgumentException: navigation destination
com.example.test/actionNavigateToNextFragment is unknown to this NavController
This could be resolved, if I had access to mBackStack field in NavController, but it's package private, and I can't use reflection in my current project. But if it was public, I would use it the following way:
override fun onBackPressed() {
// 2 because first one is NavGraph, second one is last fragment on the stack
if(navController.mBackStack.size > 2) {
navController.navigateUp()
return
}
showQuitDialog()
}
Is there a way to do this without reflection?
You can compare the ID of the start destination with the ID of the current destination. Something like:
override fun onBackPressed() = when {
navController.graph.startDestination == navController.currentDestination?.id -> showQuitDialog()
else -> super.onBackPressed()
}
Hope it helps.
Try this for any destination (you can find your destination id in the navigation graph):
private fun isDesiredDestination(): Boolean {
return with(navController) {
currentDestination == graph[R.id.yourDestinationId]
}
}
Comparing IDs of destination may not be the best solution because you may have multiple screens with the same ID but different arguments on the backstack.
Here's an alternative:
val isRootScreen = navController.previousBackStackEntry == null
if you want this is a button so to speak you can have this
yourbutton.setOnClickListener {
val currentFragment = navController.currentDestination?.id
when (navController.graph.startDestination == currentFragment) {
true -> { //Go here}
// Go to the app home
else -> onBackPressed()
}
}
In the activity class, I set up the actionbar as this:
MyActivity
setSupportActionBar(findViewById(R.id.toolbar_my))
supportActionBar?.apply {
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)
}
Because I need to override onOptionsItemSelected(...) (in the fragment class), I didn't override onSupportNavigateUp() here.
This activity cotnains a fragment. What I want is, when click on the actionbar up button, besides pop back, also revoke a custom save() method.
So in the fragment's onOptionsItemSelected(...), write some code for the item.id == android.R.id.home case. However, I made a break point here, and found that when click on the up/home button, the code in the android.R.id.home case is never revoked. The other items' on selected methods work.
In the fragment class:
MyFragment
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item?.itemId) {
android.R.id.home -> {
// code here not gets called when click up/home button
mPresenter.save()
return true
}
R.id.edit-> {
// The code here is revoked when item selected.
}
else -> {
return super.onOptionsItemSelected(item)
}
}
}
I tried override another onOptionsItemSelected(...) method in the activity class, and write android.R.id.home case, still cannot invoke methods in it.
Why the code in item.id == android.R.id.home case is not called?
Read setHomeButtonEnabled
Enable or disable the "home" button in the corner of the action bar.
(Note that this is the application home/up affordance on the action
bar, not the systemwide home button.)
supportActionBar?.setHomeButtonEnabled(true)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
Then
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.getItemId()){
android.R.id.home -> {
mPresenter.save()
return true
}
R.id.edit-> {
// some code
}
}
return super.onOptionsItemSelected(item)
}