Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The best way to handle back behavior on DialogFragment?

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.

like image 744
RobGThai Avatar asked Dec 17 '13 09:12

RobGThai


People also ask

How do I destroy DialogFragment?

tl;dr: The correct way to close a DialogFragment is to use dismiss() directly on the DialogFragment. Control of the dialog (deciding when to show, hide, dismiss it) should be done through the API here, not with direct calls on the dialog. Dismiss the fragment and its dialog.

What is the difference between dialog and DialogFragment?

Dialog: A dialog is a small window that prompts the user to make a decision or enter additional information. DialogFragment: A DialogFragment is a special fragment subclass that is designed for creating and hosting dialogs.

How do you show DialogFragment?

Showing the DialogFragment It is not necessary to manually create a FragmentTransaction to display your DialogFragment . Instead, use the show() method to display your dialog. You can pass a reference to a FragmentManager and a String to use as a FragmentTransaction tag.

What is a DialogFragment?

Android DialogFragments. DialogFragment is a utility class which extends the Fragment class. It is a part of the v4 support library and is used to display an overlay modal window within an activity that floats on top of the rest of the content. Essentially a DialogFragment displays a Dialog but inside a Fragment.


3 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();
    }
}
like image 126
gunar Avatar answered Oct 20 '22 00:10

gunar


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.

like image 39
user Avatar answered Oct 19 '22 22:10

user


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
        }
    };
}
like image 36
prince_sachdeva Avatar answered Oct 19 '22 23:10

prince_sachdeva