Home » Android » android – The best way to handle back behavior on DialogFragment?

android – The best way to handle back behavior on DialogFragment?

Posted by: admin June 15, 2020 Leave a comment

Questions:

So I have a DialogFragment displaying Area selection list. Once user click on the area, the list refetch data to display Street selection list. At this point, I’d like to allow user to press hardware back button to return to Area selection. Is this possible? I tried to override a few methods below but I can only tap to the event but not stopping it from happening.

@Override
public void onCancel(DialogInterface dialog) {
    if(isDismissable()){
        super.onCancel(dialog);
    }else {
        Log.d(TAG, "Don't dismiss cancel this dialog!");
    }
}

@Override
public void dismissAllowingStateLoss() {
    if(isDismissable()){
        super.dismissAllowingStateLoss();
    }else {
        Log.d(TAG, "Don't dismiss this dialog!");
    }
}

@Override
public void dismiss() {
    if(isDismissable()){
        super.dismiss();
    }else {
        Log.d(TAG, "Don't dismiss this dialog!");
    }
}

dismiss() get called when user press back button but even if I don’t call super.dismiss(), dialog get dismissed regardless.

Is there a way to do this? I also look into how Google+ app is displaying ActionBar in the DialogFragment to provide HomeAsUp but I can’t find any info on it.

How to&Answers:

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();
    }
}

Answer:

The best way to handle back behavior on DialogFragment?

The best way to handle the back behavior on a DialogFragment would be to not mess with it and leave it as it is, rethinking in the process your current approach. If I’m seeing a dialog in an app and I’m hitting back I’m expecting to pop the dialog out of the screen and not navigate between the dialog’s pages(I hope I’ve not misread your question).

Is there a way to do this?

In your DialogFragment you could use a custom Dialog(I’m assuming you’re just using the onCreateDialog() callback to return the Dialog with the lists) for which you override the onKeyDown() callback to handle the back being pressed:

public class CustomDialog extends Dialog {

    protected CustomDialog(Context context) {
        super(context);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
            // the back key was pressed so do something?
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

}

I also look into how Google+ app is displaying ActionBar in the
DialogFragment to provide HomeAsUp but I can’t find any info on it.

You can always make an Activity look like a Dialog.

Answer:

The best way is to override onBackPressed() in the dialog , you created in onCreateDialog().
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    return new Dialog(getActivity(), getTheme()){
        @Override
        public void onBackPressed() {
            //do your stuff
        }
    };
}