Android - Determinate ProgressDialog inside DialogFragment

650 views Asked by At

I know that Google's Material Design guidelines don't recommend using a ProgressDialog, instead using another less intrusive way to display progress, but I need to use a ProgressDialog for a specific Activity of my app.

So, the thing is that I want to wrap a ProgressDialog inside a DialogFragment, and thus my code is as follows:

public class MaterialProgressDialogFragment extends DialogFragment {

private int total;
private MaterialDialog myDialog;

@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    return myDialog;
}

public void incrementProgress(int by) {
    myDialog.incrementProgress(by);
}

public void setTotal(int total) {
    this.total = total;
}

public void setUp(Context context) {
    myDialog = new MaterialDialog.Builder(context)
            .title("Progress")
            .content("Processing...")
            .progress(false, total, true)
            .build();
}

}

Because what I want to build is a determinate ProgressDialog, I want to be able to update its progress throghout the life of my app. For this I have made a method called setProgress(progress), but myDialog is always null, as well as the value returned from getDialog().

What am I doing wrong?

Thank you

EDIT: I'm showing the dialog inside my fragment's onCreateActivity() method, like follows:

MaterialProgressDialogFragment dialogFragment = new MaterialProgressDialogFragment();
dialogFragment.setTotal(100);
dialogFragment.setUp(getActivity());
dialogFragment.show(getSupportFragmentManager(), "");
dialog.incrementProgress(50);

Everything works as expected until the last line, which causes the app to throw an exception.

1

There are 1 answers

1
Georgiy Shur On BEST ANSWER

It isn't totally clear what the variable dialog inside setProgress(int progress) method is. But if you mean getDialog() by that, it takes time to create dialog by dialog fragment, and between the dialogFragment.show(getSupportFragmentManager(), "") and onCreateDialog(Bundle savedInstanceState) callback there will be some time interval, during which getDialog() will return null.

EDIT: Ok, now it is more clear. But by the code above, you're violating the fragment framework rules. You should create your dialog in onCreateDialog(Bundle savedInstanceState) method, because otherwise you'll have problems with lifecycle (for example, if you'll rotate your screen, the app will crash).

I suggest you to use something like that:

public class MaterialProgressDialogFragment extends DialogFragment {

    public static final String TOTAL_KEY = "total";

    public static ProgressDialogFragment newInstance(int total) {
        Bundle args = new Bundle();
        args.putInt(TOTAL_KEY, total);
        ProgressDialogFragment pdf = new ProgressDialogFragment();
        pdf.setArguments(args);
        return pdf;
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        MaterialDialog myDialog = new MaterialDialog.Builder(context)
                .title("Progress")
                .content("Processing...")
                .progress(false, getTotal(), true)
                .build();
        return myDialog;
    }

    public void incrementProgress(int by) {
        if (getDialog()!=null)
            ((MaterialDialog)getDialog()).incrementProgress(by);
    }

    public int getTotal() {
        return getArguments().getInt(TOTAL_KEY);
    }
}

You should save total variable to arguments, because it will be destroyed on configuration change (for example screen rotation).

Then just create and show it by:

MaterialProgressDialogFragment dialogFragment = MaterialProgressDialogFragment.newInstance(100);
dialogFragment.show(getSupportFragmentManager(), "");

When you want to change your progress, call:

dialog.incrementProgress(50);

But remember, dialog won't be created immediately, so if you'll call this right after show(), it won't take effect, because getDialog() will return null. If you want to just test it, call it delayed:

new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                dialog.incrementProgress(50);
            }
        }, 200);

But anyway in real app you will change your progress from some background process.