BottomSheet does not collapse when I press the Back Button - android

My bottomSheet behaves correctly except in this situation. When I return to the activity via 'back button', I want the bottomSheet to collapse and I thought the code below would do the trick, but it doesn't work. What could be the cause ? (I confirmed with debugger that it reaches the statement)
#Override
public void onBackPressed() {
mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}
the same line works fine when it returns via finish():
if (resultCode == Activity.RESULT_OK) {
mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}

If your dialog has setCancelable(true), back button will not trigger the onbackpressed(), you can try this
I have a class call BottomSheetFragmentDialog which is extend from BottomSheetDialogFragment and I have setCanceledOnTouchOutside(false) inside the onCreateDialog Method also overrdie the onCancel()
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.setCanceledOnTouchOutside(false)
return dialog
}
override fun onCancel(dialog: DialogInterface) {
super.onCancel(dialog)
Toast.makeText(context, "Break Point Here", Toast.LENGTH_SHORT).show()
}

you can use this code onBackPressed() methode
behavior.setState(BottomSheetBehavior.STATE_HIDDEN);

Simpler solution.
Don't have to override onBackPressed method, just have to remove setCancelable(true) and add bottomSheetDialog.setCanceledOnTouchOutside(false) when you initialise the bottomSheetDialog.

Related

Unable to dismiss BottomSheetDialog

I'm using following approach to create bottomsheet dialog, dialog is creating but I want to dismiss on backpress for that all setup I'm using the code below. But not doing able to dismiss dialog.
class MainActivity : BaseClass(), View.OnClickListener {
private lateinit var bottomSheetDialog: BottomSheetDialog
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bottomSheetDialog=BottomSheetDialog(this)
create.setOnClickListener {
createBottomSheetDialog()
}
}
override fun onBackPressed() {
if (bottomSheetDialog.isShowing){
bottomSheetDialog.dismiss()
}
super.onBackPressed()
}
private fun createBottomSheetDialog(){
bottomSheetDialog.setContentView(R.layout.bottom_sheet_dialog)
bottomSheetDialog.setCancelable(false)
bottomSheetDialog.show()
}
}
I have been tried using this.bottomSheetDialog.dismiss()
but not working I also have try dismissing the dialog without if statement and without using super.onBackPressed() but not working.
you can simply remove this line from your code
bottomSheetDialog.setCancelable(false)
You can get more details about this here. I guess you need to stop dismiss when clicked anywhere outside the fragment so you can add this :-
bottomSheetDialog.setCanceledOnTouchOutside(false)
It will allow you to dismiss onBackPressed but it won't allow you to dismiss if clicked anywhere outside of your bottom sheet.
Also no need to write this in your Main activity :-
override fun onBackPressed() {
if (bottomSheetDialog.isShowing){
bottomSheetDialog.dismiss()
}
super.onBackPressed()
}
Bottom Sheet dialogue will itself dismiss onBackPressed.
Try this:
override fun onBackPressed() {
if (bottomSheetDialog.isShowing){
bottomSheetDialog.dismiss()
} else {
super.onBackPressed()
}
}
Try setting global variable for activity dialog: BottomSheetDialog? = null
Then in the function for displaying dialog give it a value and show it. Then in onCreate (or whenever you want) just use global variable as a reference eg.:
onBackPressed(){
if(dialog != null){
dialog.dismiss()}}

DialogFragment ignores OnBackPressedDispatcher

I'm trying to handle the back button in a BottomSheetDialogFragment, which is a DialogFragment, using 'androidx.activity:activity-ktx:1.1.0-alpha01' and 'androidx.fragment:fragment-ktx:1.2.0-alpha01'.
handleOnBackPressed() is not called and the DialogFragment is dismissed. The OnBackPressedCallback is enabled when the back button is pressed.
I think the DialogFragment is intercepting the back button press, because the ComponentActivity never calls mOnBackPressedDispatcher.onBackPressed();
Is there a way to override the DialogFragment handling of back button press?
It's really hard to understand what Google Android Dev making. Anyway, i found a solution without using interfaces.
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return object : Dialog(requireContext(), theme) {
override fun onBackPressed() {
// handle back press
}
}
}
Just override the onCreateDialog and also override onBackPressed inside it.
I found a solution, but I hope the library will take care of this usecase.
Create a custom BottomSheetDialog:
class BackPressBottomSheetDialog(context: Context, #StyleRes theme: Int,
private val callback: BackPressBottomSheetDialogCallback) :
BottomSheetDialog(context, theme) {
override fun onBackPressed() {
if (callback.shouldInterceptBackPress()) callback.onBackPressIntercepted()
else super.onBackPressed()
}
}
And its interface
interface BackPressBottomSheetDialogCallback {
fun shouldInterceptBackPress(): Boolean
fun onBackPressIntercepted()
}
Then in your BottomSheetDialogFragment
private val dialogCallback = object : BackPressBottomSheetDialogCallback {
override fun shouldInterceptBackPress(): Boolean {
//TODO should you intercept the back button?
}
override fun onBackPressIntercepted() {
//TODO what happens when you intercept the back button press
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return BackPressBottomSheetDialog(requireContext(), theme, dialogCallback)
}
My solution for Kotlin:
Override below method in BottomSheetDialogFragment.
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return super.onCreateDialog(savedInstanceState).apply {
setOnKeyListener { _: DialogInterface, keyCode: Int, keyEvent: KeyEvent ->
if (keyCode == KeyEvent.KEYCODE_BACK && keyEvent.action == KeyEvent.ACTION_UP) {
// your code
// return true if you want keep dialog visible
// return false if you want to dismiss dialog anyway
return#setOnKeyListener true
}
return#setOnKeyListener false
}
}
}
One more solution:
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return super.onCreateDialog(savedInstanceState).also {
val componentDialog = it as ComponentDialog
componentDialog.onBackPressedDispatcher.addCallback(this) {
val backPressedConsumed = yourLegacyLogic()
if (backPressedConsumed.not()) {
isEnabled = false
requireActivity().onBackPressedDispatcher.onBackPressed()
isEnabled = true
}
}
}
}
Explanation
Back-pressed events are passed into the dialog of the DialogFragment, therefore you should register OnBackpressedCallback for the dialog.
Disabling the callback (isEnabled = false) allows you to prevent the consumption of the next back-pressed event.
Once you disabled the callback you're retriggering the back pressed-event (calling requireActivity().onBackPressed()) and it will be consumed by someone else, e.g. your navigation will pop the back stack.
After that, you should enable the callback (isEnabled = true) to receive the next back-pressed events.

BottomSheetDialogFragment disable animation when returning from background

Got a class that extends BottomSheetDialogFragment
When I set the dialog to show, the slide from bottom animation occurs and the dialog is shown. Now, If I set the app to background and then bring it back to the foreground, the dialog that was already showing does the same slide in animation.
How can I disable this, that is, if the dialog is already showing, sending the app to background and then foreground does not start the animation?
Showing the dialog like this:
dialog = MyDialogFragment()
dialog?.run {
val args = Bundle()
....
arguments = args
show(this#MyFragment.fragmentManager, tag)
}
Where the dialog class only has this:
class MyDialogFragment : BottomSheetDialogFragment() {
var listener: Listener? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// inflate view
// Given the arguments, sets up the ui
return view
}
interface Listener {
...
}
}
And that's all the code that I have for the dialog.
Combining #nntk and #CKotlin answers made the trick for me. I manage to fix the issue with this code :
override fun onStop() {
super.onStop()
dialog?.window?.setWindowAnimations(-1)
}
Edit:
On Android < P (API 28), it makes the app not responding to touch events. So either you don't use it or you need to surround it with a check:
override fun onStop() {
super.onStop()
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
dialog?.window?.setWindowAnimations(-1)
}
}
overwrite onStop() method in BottomSheetDialogFragment
such as
#Override
public void onStop() {
super.onStop();
getDialog().show(); // important
}
I'm dealing with the same issue. I haven't fully been able to solve this, but I was able to stop the slide up animation from occurring on resume by doing the following:
override fun onResume() {
super.onResume()
// Disable dialog window animations for this instance
dialog?.window?.setWindowAnimations(-1)
}
This doesn't stop the background fade from animating though, so the background shade shows, disappears, and then shows again.

How to override onCancel in Dialog Fragment in Android in kotlin?

I want to show a progress bar DialogFragment.
It would be shown until either it is cancelled or dismissed.
It can be cancelled if the user either presses back button or touches outside of the dialog, and is dismissed if the user doesn't cancel it before the completion of the task.
So, I want to set listeners for both so I can respond according to the case.
The dialog is being called from a Fragment.
According to this, I can't set listeners, instead I have to override the methods.
My main problem is, I don't know how to do that in kotlin.
I have written some of the code below but it is incomplete. Please correct the code where needed.
I am trying to implement only onCancel for now. Please do tell if onDismiss is needed to be implemented in some different way.
Following the solution here,
this is how I have coded the Fragment:
class MyFragment: Fragment(), DialogInterface.OnCancelListener {
// other code
private fun myFun() {
// show progress dialog
val myDialog = DialogProgress()
myDialog.show(childFragmentManager, "null")
// todo the long task of downloading something
// myDialog.dismiss()
}
override fun onCancel(dialog: DialogInterface?) {
// User canceled the dialog
Toast.makeText(activity, "Process canceled by user!", Toast.LENGTH_SHORT).show()
// todo
}
}
And this is my DialogFragment code:
class DialogProgress: DialogFragment() {
override fun onCancel(dialog: DialogInterface?) {
super.onCancel(dialog)
// need help here
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
super.onCreateDialog(savedInstanceState)
// show progress dialog
val v = activity!!.layoutInflater.inflate(R.layout.dialog_progress, null)
v.findViewById<TextView>(R.id.progress_message).text = "Fetching data"
return activity!!.let {
val builder = AlertDialog.Builder(it, R.style.ThemeOverlay_AppCompat_Dialog)
builder
.setView(progressView)
.create()
}
}
}
For the above code where I need help, I don't know how to convert the following Java code from the link of solution given above into kotlin:
#Override
public void onDismiss(final DialogInterface dialog) {
super.onDismiss(dialog);
Fragment parentFragment = getParentFragment();
if (parentFragment instanceof DialogInterface.OnDismissListener) {
((DialogInterface.OnDismissListener) parentFragment).onDismiss(dialog);
}
}
Note that this is for onDismiss, I want it for onCancel.
The Java code can be simply converted into kotlin code by pasting it into the Android Studio and a pop-up should appear.
This is the conversion of the java code:
override fun onCancel(dialog: DialogInterface?) {
super.onCancel(dialog)
val parentFragment = parentFragment
if (parentFragment is DialogInterface.OnCancelListener) {
(parentFragment as DialogInterface.OnCancelListener).onCancel(dialog)
}
}

Destroy DialogFragment on onCreateDialog()

I have a dialog fragment that initializes Google plus views, sometimes those views fail so I'd like to kill the dialog at that point, before it's displayed to the user.
How can I end the dialog creation process? returning null from onCreateDialog which returns a Dialog object crushes the program.
If you'd like to dismiss DialogFragment within onCreateDialog you can do the following:
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
setShowsDialog(false);
dismiss();
return null;
}
No need to override onActivityCreated().
Solved it using the onActivityCreated() Fragment callback which is called after OnCreateDialog(). I return a valid Dialog from onCreateDialog() but flag with dismiss that the dialog should be dismissed.
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(dismiss) {
this.dismiss();
}
}
The other answers are a bit outdated and one didn't work for me (wrote there my comments about it), so here's what I think is updated and working:
On the onCreateDialog callback, have your logic of when it succeeds. If failed, return some default dialog (won't be used anyway) while adding to the lifecycle's onStart callback to dismiss the DialogFragment (use dismiss or dismissAllowingStateLoss):
fun Lifecycle.runOnStarted(runnable: () -> Unit) {
addObserver(object : DefaultLifecycleObserver {
override fun onStart(owner: LifecycleOwner) {
super.onStart(owner)
runnable.invoke()
}
})
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
//create the builder of the dialog, and then check if need to dismiss
if (needToDismiss) {
lifecycle.runOnStarted{
dismissAllowingStateLoss()
}
return builder.create()
}
Alternative, using kotlin coroutines and without using the helper function I've made:
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
//create the builder of the dialog, and then check if need to dismiss
if (needToDismiss) {
lifecycleScope.launch {
whenStarted {
dismissAllowingStateLoss()
}
}
return builder.create()
}
Or you can use this helper function that uses kotlin coroutines to make it a bit shorter:
fun LifecycleOwner.runOnStarted(runnable: () -> Unit) {
lifecycleScope.launch {
whenStarted{
runnable.invoke()
}
}
}
The usage would be as short as before:
runOnStarted{
dismissAllowingStateLoss()
}
Note that I use onStart callback instead of onCreate. The reason is that for some cases, onStart works, while onCreate won't.

Categories

Resources