BasicDialog

Difference between a BasicDialog and an AlertDialog

Dialogs in Android1 are used to shows alerts for making decisions or to edit a single value2.
But there are some differences between an AlertDialog and a BasicDialog.
In an AlertDialog you always want to show a message and at least one Button for user interaction.
In a BasicDialog you have a custom view to a TextView or something more complex.

The first working example was quickly done, thanks to the Android Developers Guide3.
But I was a little bit afraid of getting a warning in Android Studio and in the Lint Report.

Avoid passing null as the view root (needed to resolve layout parameters on the inflated layout’s root element)

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
  LayoutInflater inflater = getActivity().getLayoutInflater();
  View view = inflater.inflate(R.layout.fragment_edit_name, null);
  ButterKnife.inject(this, view);

Help I’m using a BasicDialog, but what went wrong here?

The solution looked fine and suggests to use the AlertDialog.Builder for a BasicDialog, but I think this example is just outdated and we can do better!
And we can, looking into the API-Reference we find a better example4.
So lets get started and have a look at the „wrong“ implementation first!

Example: Layout Inflation without a Parent“

I made an working example you can look at Github((https://github.com/avalax/dialogfragment/releases/tag/v1.0)) where the AlertDialog.Builder is used to build with a custom layout.

package de.avalax.fitbuddy.presentation.dialog;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;

import butterknife.ButterKnife;
import butterknife.InjectView;
import de.avalax.fitbuddy.R;

public class EditNameDialogFragment extends DialogFragment {
    private static final String ARGS_NAME = "name";
    private static final String ARGS_HINT = "hint";
    @InjectView(R.id.nameEditText)
    protected EditText nameEditText;
    private DialogListener listener;

    public static EditNameDialogFragment newInstance(String name, String hint) {
        EditNameDialogFragment fragment = new EditNameDialogFragment();
        Bundle args = new Bundle();
        args.putString(ARGS_NAME, name);
        args.putString(ARGS_HINT, hint);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            listener = (DialogListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement EditNameDialogFragment.DialogListener");
        }
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        LayoutInflater inflater = getActivity().getLayoutInflater();
        View view = inflater.inflate(R.layout.fragment_edit_name, null);
        ButterKnife.inject(this, view);

        String name = getArguments().getString(ARGS_NAME);

        nameEditText.setText(name);
        nameEditText.setHint(getArguments().getString(ARGS_HINT));

        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setView(view)
                .setTitle(R.string.dialog_change_name)
                .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        listener.onDialogPositiveClick(EditNameDialogFragment.this);
                    }
                })
                .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        EditNameDialogFragment.this.getDialog().cancel();
                    }
                });
        return builder.create();
    }

    public String getName() {
        return nameEditText.getText().toString();
    }

    public interface DialogListener {
        public void onDialogPositiveClick(EditNameDialogFragment editNameDialogFragment);
    }
}

In the layout we only have the EditText itself.

<?xml version="1.0" encoding="utf-8"?>
<EditText
    android:id="@+id/nameEditText"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:ignore="labelFor"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:inputType="textNoSuggestions" />

And why should we change a working solution?

At first I asked me this question, but there are some reasons why we should change this:

  • Make our code more readable
  • Move the styling of the dialog to the layout-file
  • Remove the warning from the linter

The AlertDialog.Builder is removed and we are using the onCreateView to inflate our layout.
I’m using ButterKnife5 for view injection and OnClick events.

package de.avalax.dialogfragment;

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;

import butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnClick;

public class BasicDialogFragment extends DialogFragment {

    private static final String ARGS_NAME = "alertText";
    @InjectView(R.id.nameEditText)
    protected EditText nameEditText;
    private DialogListener listener;

    public static BasicDialogFragment newInstance(String name) {
        BasicDialogFragment fragment = new BasicDialogFragment();
        Bundle args = new Bundle();
        args.putString(ARGS_NAME, name);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            listener = (DialogListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement BasicDialogFragment.DialogListener");
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_basic_dialog, container, false);
        ButterKnife.inject(this, view);
        getDialog().setTitle(R.string.dialog_change_name);
        nameEditText.setText(getArguments().getString(ARGS_NAME));
        return view;
    }

    @OnClick(R.id.done_button)
    protected void positiveButton() {
        listener.onDialogPositiveClick(BasicDialogFragment.this);
        getDialog().dismiss();
    }

    public String getAlertText() {
        return nameEditText.getText().toString();
    }

    public interface DialogListener {
        public void onDialogPositiveClick(BasicDialogFragment basicDialogFragment);
    }
}

The Button creation is moved to the layout and uses a custom style. In this step I decided to remove the cancel action.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:minWidth="400dip"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <EditText
        tools:ignore="labelFor"
        android:id="@+id/nameEditText"
        android:layout_width="fill_parent"
        android:layout_height="0.0dip"
        android:inputType="textNoSuggestions"
        android:divider="@null"
        android:dividerHeight="0.0dip"
        android:layout_weight="1.0" />

    <Button
        android:id="@+id/done_button"
        android:layout_gravity="end"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/done_label"
        style="?android:buttonBarButtonStyle" />
</LinearLayout>

Building the project using Gradle and look at the lint result again and we won’t see any warnings, yay.

Conclusion

Some little changes but we have a great improvement in our source code.
Compared to the old solution, we have a much better cut between the layout and the Java source.
We have moved the labels to the layout and have a method for the OnClick event.
You can grab the source-code for the final version at Github6.

  1. http://developer.android.com/guide/topics/ui/dialogs.html []
  2. http://developer.android.com/design/building-blocks/dialogs.html []
  3. http://developer.android.com/guide/topics/ui/dialogs.html#CustomLayout []
  4. http://developer.android.com/reference/android/app/DialogFragment.html []
  5. http://jakewharton.github.io/butterknife/ []
  6. https://github.com/avalax/dialogfragment/releases/tag/v2.0 []
Summary
Difference between a BasicDialog and an AlertDialog
Article Name
Difference between a BasicDialog and an AlertDialog
Description
When using a DialogFragment there are major differences between an AlertDialog and a BasicDialog. See an complete examples using Gradle and Material Design.
Author