Detect Back Button But Don't Dismiss Dialogfragment

DialogFragment and back button

It's hard to say for sure what the issue is, since you haven't posted any code. But my first guess is that you haven't added the DialogFragment to the back stack by calling the addToBackStack method of the FragmentTransaction that you're using to add your fragment to the activity.

There are examples right in the Android documentation pages that give examples of a good pattern for using a DialogFragment in your Activity.

Since you are displaying a Dialog, the created Dialog will receive the key events, not the parent Activity. So, set a Dialog.OnKeyListener when you create the Dialog's fragment, and call setCancelable(false) on the Dialog to prevent the back key from dismissing it. You can then handle the back key in your OnKeyListener's onkey method.

Preventing the back button from cancelling a DialogFragment

How about using setCancelable? Did you try it?

From the Docs:

Control whether the shown Dialog is cancelable. Use this instead of
directly calling Dialog.setCancelable(boolean), because DialogFragment
needs to change its behavior based on this

For custom DialogFragment

Add isCancelable = false at onCreateDialog

The best way to handle back behavior on DialogFragment?

I see two solutions:

The simplest: Have Area selection and Street selection list as both separate usual fragments and have both of them in a separate activity and have this activity as a dialog through a simple theme:
<activity android:theme="@android:style/Theme.Dialog" /> and have excludeFromRecents="true" to not have this in the recents used apps.
Area selection is loaded first, then add street selection through addToBackStack(null) so you'll have AreaSelection fragment underneath.

If you don't want to have a separate activity for this for any reason, you could add a dialog listener from your dialogfragment and its implementor (the activity) will open the AreaFragment. With a basic understanding of your code this simple project should do it:

The owner activity:

import com.example.adip.fragments.AreaSelectionFragment;
import com.example.adip.fragments.StreetSelectionFragment;
import com.example.adip.fragments.AreaSelectionFragment.AreaSelectionListener;
import com.example.adip.fragments.StreetSelectionFragment.StreetSelectionListener;

import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.view.View.OnClickListener;

public class DialogsActivity extends FragmentActivity implements OnClickListener,
AreaSelectionListener, StreetSelectionListener {

private static final String AREA_TAG = "AREA_TAG";

private static final String STREETS_TAG = "STREETS_TAG";

@Override
protected void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
setContentView(R.layout.area_selections);
findViewById(R.id.btnStuff).setOnClickListener(this);
}

@Override
public void onClick(View v) {
showArea();
}

private void showArea() {
DialogFragment df = new AreaSelectionFragment();
df.show(getSupportFragmentManager(), AREA_TAG);
}

@Override
public void onStreetsUserCanceled() {
showArea();
}

@Override
public void showStreets() {
DialogFragment df = new StreetSelectionFragment();
df.show(getSupportFragmentManager(), STREETS_TAG);
}

}

AreaSelectionFragment (extend it to your needs):

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;

public class AreaSelectionFragment extends DialogFragment {
public static interface AreaSelectionListener {
void showStreets();
}

private AreaSelectionListener areaSelectionListener;

@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity instanceof AreaSelectionListener) {
areaSelectionListener = (AreaSelectionListener) activity;
} else {
throw new ClassCastException("Parent Activity must implement AreaSelectionListener");
}
}

@Override
public void onDetach() {
super.onDetach();
areaSelectionListener = null;
}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity()).setTitle("Area Selection")
.setPositiveButton("OK", new OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
areaSelectionListener.showStreets();
}
}).setNegativeButton("Cancel", null).create();
}
}

And StreetSelectionFragment (again: extend it to your needs):

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;

public class StreetSelectionFragment extends DialogFragment {
public static interface StreetSelectionListener {
void onStreetsUserCanceled();
}

private StreetSelectionListener selectionListener;

@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity instanceof StreetSelectionListener) {
selectionListener = (StreetSelectionListener) activity;
} else {
throw new ClassCastException("Parent activity must implement StreetSelectionListener");
}
}

@Override
public void onDetach() {
selectionListener = null;
super.onDetach();
}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = new AlertDialog.Builder(getActivity()).setTitle("Street Selection")
.setPositiveButton("OK", null).setNegativeButton("Cancel", new OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
selectionListener.onStreetsUserCanceled();
}
}).create();
return dialog;
}

@Override
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
selectionListener.onStreetsUserCanceled();
}
}

Prevent DialogFragment from dismissing when clicking outside/back button, and show a message asking for confirmation instead

Found the solution, credit to @Unknownweirdo, How to dismiss the dialog with click on outside of the dialog?

Kotlin code:

    override fun onResume() {
super.onResume()

dialog?.window?.addFlags(
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
)

dialog?.window?.decorView?.setOnTouchListener { v, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
val dialogBounds = Rect()
v.getHitRect(dialogBounds)
if (!dialogBounds.contains(event.x.toInt(), event.y.toInt())) {
// You have clicked the grey area
logicToConfirmDiscardingChange()
false // stop activity closing
}
}
true
}
}

Mvvmcross 8.0.2 Android MvxDialogFragment not close with back button

This seems like a bug. You now need to add [MvxDialogFragmentPresentation(Cancelable = true)] to enable closing a dialog with the back button. The documentation states Cancelable should default to true.

dialogFragment doesn't dismiss when clicking close to it's edge

I'd been facing this issue myself, so did some digging through the source code. Turns out it's intentional behaviour, and is called "touchSlop". It's defined in ViewConfiguration:

https://developer.android.com/reference/android/view/ViewConfiguration.html#getScaledWindowTouchSlop()

The offending code is in the Window class:

public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
&& isOutOfBounds(context, event) && peekDecorView() != null) {
return true;
}
return false;
}

Which then calls:

private boolean isOutOfBounds(Context context, MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
final int slop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
final View decorView = getDecorView();
return (x < -slop) || (y < -slop)
|| (x > (decorView.getWidth()+slop))
|| (y > (decorView.getHeight()+slop));
}

The value of which comes from:

/**
* Distance in dips a touch needs to be outside of a window's bounds for it to
* count as outside for purposes of dismissing the window.
*/
private static final int WINDOW_TOUCH_SLOP = 16;

I can't find any way to override this behaviour or change the slop value. I think the only option would be to implement a full screen dialog with a transparent background and a manual click handler. I've decided that it's not a good idea for my app to override default system behaviour, so I'm not going to implement it.



Related Topics



Leave a reply



Submit