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()
}
}
Related
I have a simple android app based on the Basic Activity. I have 6 fragments that are displayed in the Main Activity depending on the state of a finite state machine (https://github.com/Tinder/StateMachine).
The state machine changes state based on input from the different fragments, and also on background processes. For example, if the app looses contact with a bluetooth connected device, then the state changes, and so does the fragment displayed in the Main Activity. I have a Settings Activity with a few options, as well.
So far, all the code works. When the state changes, whether via background processes or button clicks on the currently displayed fragment, the screen changes correctly.
My question relates to the toolbar back button in the Settings Activity. The back button returns to the correct Main Activity fragment, except if the state changes while the I am viewing the Settings Activity. I am not sure how to get the Settings Activity toolbar back button to go to the correct fragment in the Main Activity when a background process change the current state.
I launch the Settings Activity in the Main Activity using:
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_settings -> {
// launch settings activity
startActivity(Intent(this#MainActivity, SettingsActivity::class.java))
return true
}
else -> super.onOptionsItemSelected(item)
}
}
In the Settings Activity I have:
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
LOG.info("trying to get home")
finish()
true
}
else -> super.onOptionsItemSelected(item)
}
}
I have this code in the Main Activity to change the fragment based on the new state of the state machine:
fun renderViewState(newTransition: RLSideEffect, message: String) {
LOG.info("MainActivity: renderView: newTransition=$newTransition")
val navController = findNavController(R.id.nav_host_fragment_content_main)
when (newTransition) {
is RLSideEffect.LogStart -> {
LOG.info("MainActivity: LogStart")
navController.navigate(R.id.action_StartFragment)
toast { message }
}
is RLSideEffect.LogConnected -> {
LOG.info("MainActivity: Connected")
navController.navigate(R.id.action_ConnectedFragment)
toast { message }
}
is RLSideEffect.LogContinuityOK -> {
LOG.info("MainActivity LogContinuityOK")
navController.navigate(R.id.action_ContinuityOkFragment)
toast { message }
}
is RLSideEffect.LogContinuityFailed -> {
LOG.info("MainActivity: LogContinuityFailed")
navController.navigate(R.id.action_ContinuityFailedFragment)
toast { message }
}
is RLSideEffect.LogArmed -> {
LOG.info("MainActivity: LogArmed")
navController.navigate(R.id.action_ArmedFragment)
toast { message }
}
is RLSideEffect.LogIgnition -> {
LOG.info("MainActivity: LogIgnition")
navController.navigate(R.id.action_IgnitionFragment)
toast { message }
}
}
}
How do I get the Settings Activity to return to the current state (fragment) of the Main Activity, and not just the point the Settings Activity was launched from?
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'm currently running into a strange problem, similar to here Shared element transition when using ActionBar Back button but with a fragment to fragment shared element transition. It works fine when using the back button. As soon as the toolbar home is triggered it's just blinking. So calling finishAfterTransition() is no option here.
I call the same method both times. The base for back navigation
override fun onBackPressed() {
if (coordinator != null) {
coordinator!!.back()
} else {
finish()
}
}
and the intercepted toolbar home click.
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
return when (item?.itemId) {
android.R.id.home -> {
onBackPressed()
true
}
else -> return super.onOptionsItemSelected(item)
}
}
Update: Now I noted like one out of ten tries it works correctly.
I hope anyone of you guys has a clue why this happens.
Regards coffeelord
I want to show dialog when user press back or quit from fragment if there are some data unsaved. I am trying to override onbackpressed but unfortunately I got error lateinit property barcodeList has not been initialized. how to solve it?
here is my script on activity:
override fun onBackPressed() {
val theFragment = supportFragmentManager.fragments
for(i in 0 until theFragment.size)
{
if(theFragment[i].tag == "stocker_fragment")
{
StockerFragment().onBackPressed()
}
}
}
and this is in fragment:
fun onBackPressed() {
var check = false
// this barcodeList variable error.
for(i in 0 until barcodeList.size)
{
if(barcodeList[i].barcode.trim()=="")
{
check = true
break
}
}
if (check)
{
AlertHelper(context).onBackPressedAlert()
}
}
FYI: I have initialized barcodeList on onCreateView and everything is fine. only error in onBackPressed.
And my last question is, how do i know if user quit from fragment without pressing back button?
I think the problem is in your onBackPressed() implementation in the Activity. With the line StockerFragment().onBackPressed() you are creating a new instance of the StockerFragment and calling onBackPressed() on it, rather than calling it on the instance that is actively being used.
You should be able to adjust your Activity onBackPressed() like so:
override fun onBackPressed() {
val theFragment = supportFragmentManager.fragments
for(i in 0 until theFragment.size)
{
if(theFragment[i].tag == "stocker_fragment")
{
(theFragment[i] as StockerFragment).onBackPressed()
}
}
}
You can also make this a bit more kotliny like so:
override fun onBackPressed() {
supportFragmentManager.fragments.forEach { fragment ->
if (fragment is StockerFragment) {
fragment.onBackPressed()
}
}
}
You'll probably also want to figure out a way to decide whether the fragment's onBackPressed has determined that the Activity should stick around or not. Then, if the fragment is happy, you should call super.onBackPressed() in the Activity so that the expected back behavior (leave the Activity) happens.
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)
}