BasicDialog

Difference between a BasicDialog and an AlertDialog

Dialogs in Android 1 are used to shows alerts for making decisions or to edit a single value 2.
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 Guide 3.
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 example 4.
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 Github5 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 ButterKnife 6 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 Github 7.

  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. https://github.com/avalax/dialogfragment/releases/tag/v1.0
  6. http://jakewharton.github.io/butterknife/
  7. 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