I'm developing an app with ActionBars which supports Gingerbread and up. So basically I'm using the support library and extending
ActionBarActivity
for all my activities. Everything works well except for the
onSupportNavigateUp()
method. It just does not get called as stated in the documentation.
This method is called whenever the user chooses to navigate Up within
your application's activity hierarchy from the action bar.
This is quite easy but I haven't been able to figure out why it does not work as expected nor Googling helped. Is this a bug? or am I missing something?
if you override onOptionsItemSelected, then onSupportNavigateUp will not be called.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// handle ⬅️ button here
break;
}
return true;
}
As said before, if you override OnOptionsItemSelected, OnSupportNavigateUp will not be called. you can make sure it gets called by adding a default: case inside the OnOptionsItemSelected like so:
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.something:
Intent intent = new Intent(this,someActivity.class);
startActivity(intent);
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
The super.onOptionsItemSelected(item) will make sure OnSupportNavigateUp is called. This is in case you don't want the case dealt inside your OnOptionsItemSelected
Have you set in your Manifest.xml file the parent activity? If yes, it won't be called. #see http://developer.android.com/reference/android/support/v7/app/ActionBarActivity.html#onSupportNavigateUp()
Who knew... onSupportNavigateUp() works only on 4.0 and above. For below onNavigateUp() is called.
It is also important to return true from onCreateOptionsMenu(), I have this set to false as I have custom menu but then onOptionsItemSelected() has not been called!
So use:
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
//menuInflater.inflate(R.menu.main, menu)
return true
}
And then
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
when (item.itemId) {
R.id.action_share -> return true
else -> return super.onOptionsItemSelected(item)
}
}
I had a same problem onSupportNavigateUp and onOptionsItemSelected was not calling
I need to prevent backPress on child fragments so I override setNavigationOnClickListener in toolbar
toolbar.setNavigationOnClickListener {
if(childFragmentHandleBackPressed()){
NavigationUI.navigateUp(
navController,
AppBarConfiguration(navController.graph, drawerLayout)
)
}
}
or you can use navController.navigateUp() if you don't want to open navigation drawer menu on start/root fragment
So actually when you override onOptionsItemSelected you should return false by default for the onSupportNavigateUp method to be called. See below
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_print -> {
doSomething()
return true
}
}
return false
}
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
You need to add this line in onCreate() method
supportActionBar?.setDisplayHomeAsUpEnabled(true)
along with on onSupportNavigateUp() method
Related
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
I want to add custom up navigation from fragment using Navigation component
In my build.gradle(app) I use androidx.appcompat:appcompat:1.1.0-alpha04 dependency to have access to onBackPressedDispatcher from activity.
So I implemented OnBackPressedCallback in my fragment and
registered callback to dispatcher:
requireActivity().onBackPressedDispatcher.addCallback(this)
I expected that pressing navigate up in toolbar will call it, but it doesn't.
Pressing device's back button calls it as expected.
Is there a similar way to add some callback in fragment on navigate up action?
UPDATE
overridden methods onOptionsItemSelected and onSupportNavigateUp doesn't invoked on pressing up button in toolbar
I found a solution
handleOnBackPressed() method invokes only on device's back button click.
I wonder, why neither onOptionsItemSelected() nor onSupportNavigateUp() methods haven't been called on pressing "up button" in toolbar. And the answer is I used
NavigationUI.setupWithNavController(toolbar, navController, appBarConfiguration)
in activity to setup toolbar with navigation component.
And that made toolbar responsive for work with navigation internally, pressing "up button" haven't invoked any of overridden methods in activity or fragments.
Instead should be used
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration)
That will make actionBar responsive for navigation, thus I can use overridden functions onOptionsItemSelected() and onSupportNavigateUp()
And best place (in my case) to add custom behavior on "up button" click for certain screen is
onSupportNavigateUp()
of hosted activity, like that
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(R.id.mainNavHostFragment)
return when(navController.currentDestination?.id) {
R.id.destinationOfInterest -> {
// custom behavior here
true
}
else -> navController.navigateUp()
}
}
But worth to say, that if you want implement custom behavior directly in fragment, answer of #Enzokie should work like a charm
You need to call onBackPressed() from onBackPressedDispatcher property. Assuming your Toolbar is properly setup you can use the code below in your Activity.
override fun onOptionsItemSelected(menuItem : MenuItem?) : Boolean {
if (menuItem.getItemId() == android.R.id.home) {
onBackPressedDispatcher.onBackPressed()
return true // must return true to consume it here
}
return super.onOptionsItemSelected(menuItem)
}
on Fragment override
override fun onAttach(context: Context) {
super.onAttach(context)
//enable menu
setHasOptionsMenu(true)
requireActivity()
.onBackPressedDispatcher
.addCallback(this){
//true means that the callback is enabled
this.isEnabled = true
exitDialog() //dialog to conform exit
}
}
What this does is :
Trigger a call to the currently added OnBackPressedCallback
callbacks in reverse order in which they were added. Only if the most
false from its OnBackPressedCallback#handleOnBackPressed()
will any previously added callback be called.
I am using AndroidX in my example therefore my import will look like
import androidx.appcompat.app.AppCompatActivity.
This set up also works and you won't need to override onSupportNavigateUp in your activity:
NavigationUI.setupWithNavController(toolbar, navController, appBarConfiguration)
toolbar.setNavigationOnClickListener {
if (navController.currentDestination?.id == R.id.destinationOfInterest) {
// Custom behavior here
} else {
NavigationUI.navigateUp(navController, configuration)
}
}
I prefer to set up the Toolbar since it will handle automatically the up navigation and open/close a DrawerLayout if you have one.
Add click event to toolbar back button in this way
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// Toolbar back button pressed, do something you want
default:
return super.onOptionsItemSelected(item);
}
}
Another way
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
// Title and subtitle
toolbar.setNavigationOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// Toolbar back button pressed, do something you want
}
});
I customized (directly in Fragment) the backbress on Toolbar by using the following steps:
1. onCreate [Activity]:
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration)
2. onSupportNavigateUp [Activity]:
override fun onSupportNavigateUp(): Boolean {
onBackPressedDispatcher.onBackPressed()
return super.onSupportNavigateUp()
}
3. Customize or disable backpress [Fragment]:
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
isEnabled = false
// enable or disable the backpress
}
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
}
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)
}