Diseño Android: Cuadros de diálogo de selección

android

Los cuadros de diálogo son un elemento básico de la mayoría de interfaces de usuario y no necesitan presentación alguna. Según las guías de estilos de Material Design, “informan a los usuarios sobre una tarea específica y pueden contener información crítica, requerir decisiones o implicar varias tareas.” En Android, son ventanas que se muestran sobre una pantalla, representada por una Activity o un Fragment, y que bloquean el uso de la pantalla subyacente. Esta ventana es gestionada directamente por una implementación del contrato android.content.DialogInterface, aunque en la práctica será suficiente con utilizar alguna de las implementaciones que ya incluye Android.

El siguiente diagrama muestra las clases e interfaces más importantes para los cuadros de diálogos proporcionadas por Android de forma nativa, esto es, sin tener en cuenta las support libraries.

Aunque no es necesario, en la actualidad la práctica habitual, y recomendada desde Google, es encapsular los cuadros de diálogo en fragments que hereden de DialogFragment.

Cursos aplicaciones móviles

En este tutorial vamos a crear cuadros de diálogo encapsulados en fragments para seleccionar uno o varios elementos de un listado utilizando los recursos ya proporcionados por Android. Este es el ejemplo que vamos a desarrollar:

Proyecto de ejemplo

El proyecto de ejemplo de este tutorial utilizará Material Design y será compatible con Jelly Bean 4.1 (Api 16) y superior. Para conseguirlo nos apoyaremos en las librerías de compatibilidad, concretamente en el módulo appcompat.

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    buildToolsVersion '26.0.1'

    defaultConfig {
        applicationId "com.danielme.android.dialogfragmentpicker"
        minSdkVersion 16
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    allprojects {
        repositories {
            jcenter()
            maven {
                url "https://maven.google.com"
            }
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:26.0.0'
}

Aplicamos un estilo Material Design y definimos el estilo de la ActionBar-Toolbar (para más información ver este tutorial).

<resources xmlns:tools="http://schemas.android.com/tools">

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">@color/primary</item>
        <item name="colorPrimaryDark">@color/primaryDark</item>
    </style>

    <style name="AppTheme.Toolbar">
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">?attr/actionBarSize</item>
        <item name="android:background">@color/primary</item>
        <item name="android:elevation" tools:targetApi="lollipop">4dp</item>
        <item name="android:theme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item>
    </style>
</resources>
El AlertDialog de Android

En primer lugar, vamos a echar un vistazo muy rápido a los elementos gráficos más importantes que pueden formar parte del componente Alert Dialog nativo de Android.

Este Alert Dialog se define utilizando un builder.

 AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        builder.setTitle("title")              
                .setMessage("message")
                .setNegativeButton("negative", null)
                .setNeutralButton("neutral", null)
                .setPositiveButton("positive", null);

builder.create().show()

A tener en cuenta:

  • Si un elemento no se define con el seter no se muestra.

  • Al pulsarse un botón el cuadro de diálogo se cierra siempre. Si a ese botón le hemos asignado un listener, se ejecutará antes de cerrarse.
  • El cuadro de diálogo se cierra al pulsarse el botón “back” de Android o fuera del cuadro de diálogo. Este comportamiento se puede configurar con el método setCancelable. Pero si el Alert Dialog está encapsulado en un DialogFragment, el método a llamar es el setCancelable del DialogFragment.
  • Si el Alert Dialog se muestra directamente desde una Activity, si esta se destruye y el AlertDialog se está mostrando se producirá un memory leak por lo que deberemos cerrar el diálogo antes invocando a dismiss.
Diálogo de selección simple

Tal y como se ha comentado en la introducción, nuestro cuadro de diálogo estará encapsulado dentro de un fragment. En lugar de implementar directamente un DialogFragment vamos a recurrir a su subclase AppCompatDialogFragment para que nuestros diálogos tengan un estilo Material Design gracias a appcompat.

En nuestro fragment, sobrescribimos el método onCreateDialog el cual devolverá el Dialog que mostrará el Fragment. Este dialog lo creamos de forma estándar, esto es, utilizando el builder que acabamos de ver. Para el caso concreto del diálogo de selección, tenemos que utilizar una de las sobrecargas del método setSingleChoiceItems.

En nuestro ejemplo, vamos a utilizar la versión de este método que recibe un array con los textos de cada opción que puede ser seleccionada, el índice en ese array del elemento preseleccionado si lo hubiera, y el listener que atiende la selección de uno de los items y que usaremos para guardar en un atributo de la clase el índice del elemento seleccionado. El resto de elementos del diálogo (título y botones) los vamos a configurar como en cualquier Alert Dialog.

Una primera versión del fragment es la que sigue.

package com.danielme.android.dialogfragmentpicker;

import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatDialogFragment;
import android.widget.Toast;

public class DialogSinglePickerFragment extends AppCompatDialogFragment {

    private static final int UNSELECTED = -1;
    private int selected;

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        final String[] brands = {getString(R.string.htc), getString(R.string.lg), getString(R.string.meizu),
                getString(R.string.motorola), getString(R.string.samsung)};

        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());

        builder.setTitle(R.string.pick_brand)
                .setSingleChoiceItems(brands, UNSELECTED, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        selected = which;
                    }
                })
                .setNegativeButton(android.R.string.cancel, null)
                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        if (selected != UNSELECTED) {
                            Toast.makeText(getContext(), brands[selected], Toast.LENGTH_SHORT).show();
                        }
                    }
                });

        return builder.create();
    }

}

Ahora vamos a añadir algunas mejoras:

  • Indicar al Fragment el elemento preseleccionado (opcional). Para ello vamos a recurrir al clásico patrón “constructor estático” debido a que los fragments siempre tienen que ser instanciados con el constructor vacío.
  • El fragment se muestra en una Activity lo que implica que al ser rotada perderemos el valor almacenado en el atributo selected. Evitaremos esto implementando el método onSaveInstanceState
  • Para hacer el Fragment más flexible y genérico, también vamos a pasar como parámetros la lista de items y el título.

El código completo del fragment queda como sigue.

package com.danielme.android.dialogfragmentpicker;

import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatDialogFragment;
import android.widget.Toast;

public class DialogSinglePickerFragment extends AppCompatDialogFragment {

    private static final int UNSELECTED = -1;
    private static final String ARG_SELECTED = "ARG_SELECTED";
    private static final String ARG_ITEMS = "ARG_ITEMS";
    private static final String ARG_TITLE = "ARG_TITLE";
    private static final String STATE_SELECTED = "STATE_SELECTED";


    private int selected;

    public static DialogSinglePickerFragment newInstance(Integer selection, String[] items, String title) {
        DialogSinglePickerFragment fragment = new DialogSinglePickerFragment();
        Bundle args = new Bundle();
        if (selection != null) {
            args.putInt(ARG_SELECTED, selection);
        }
        args.putStringArray(ARG_ITEMS, items);
        args.putString(ARG_TITLE, title);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        if (savedInstanceState != null) { //rotation
            selected = savedInstanceState.getInt(STATE_SELECTED, UNSELECTED);
        } else if (getArguments() != null) {
            selected = getArguments().getInt(ARG_SELECTED, UNSELECTED);
        }

        final String[] brands = getArguments().getStringArray(ARG_ITEMS);

        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());

        builder.setTitle(getArguments().getString(ARG_TITLE))
                .setSingleChoiceItems(brands, selected, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        selected = which;
                    }
                })
                .setNegativeButton(android.R.string.cancel, null)
                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        if (selected != UNSELECTED) {
                            Toast.makeText(getContext(), brands[selected], Toast.LENGTH_SHORT).show();
                        }
                    }
                });

        return builder.create();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(STATE_SELECTED, selected);
    }

}

Este fragment es mostrado desde la Activity con un botón.

 findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                DialogSinglePickerFragment dialogFragment = DialogSinglePickerFragment.newInstance(4, brands, getString(R.string.pick_brand));
                dialogFragment.show(getSupportFragmentManager(), DialogSinglePickerFragment.class.getSimpleName());
            }
        });

Diálogo de selección múltiple

Este fragment es muy parecido al diálogo de selección de un único elemento, tan sólo hay dos diferencias significativas:

  • Ahora usaremos el método setMultiChoiceItems en lugar de setSingleChoiceItems.
  • Para almacenar las posiciones de los items seleccionados necesitamos un array de boolean del mismo tamaño que el array de items. Este array, que en el código he denominado selected, indicará para cada índice de los items seleccionables si el item está seleccionado (true) o no (false).

    Teniendo en cuenta estas diferencias, el nuevo fragment queda como sigue.

    package com.danielme.android.dialogfragmentpicker;
    
    import android.app.Dialog;
    import android.content.DialogInterface;
    import android.os.Bundle;
    import android.support.v7.app.AlertDialog;
    import android.support.v7.app.AppCompatDialogFragment;
    import android.widget.Toast;
    
    public class DialogMultiplePickerFragment extends AppCompatDialogFragment {
    
        public static final String SELECTION = "SELECTION";
    
        private static final String ARG_SELECTED = "ARG_SELECTED";
        private static final String STATE_SELECTED = "STATE_SELECTED";
        private static final String ARG_ITEMS = "ARG_ITEMS";
        private static final String ARG_TITLE = "ARG_TITLE";
    
        private boolean[] selected;
    
        public static DialogMultiplePickerFragment newInstance(String[] items, String title, int... selection) {
            DialogMultiplePickerFragment fragment = new DialogMultiplePickerFragment();
            Bundle args = new Bundle();
            if (selection != null) {
                args.putIntArray(ARG_SELECTED, selection);
            }
            args.putStringArray(ARG_ITEMS, items);
            args.putString(ARG_TITLE, title);
            fragment.setArguments(args);
            return fragment;
        }
    
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final CharSequence[] brands = getArguments().getStringArray(ARG_ITEMS);
    
            if (savedInstanceState != null) { //rotation
                selected = savedInstanceState.getBooleanArray(STATE_SELECTED);
            } else if (getArguments() != null) {
                selected = new boolean[brands.length];
                int[] selection = getArguments().getIntArray(ARG_SELECTED);
                for (int aSelection : selection) {
                    selected[aSelection] = true;
                }
            }
    
            AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
    
            builder.setTitle(getArguments().getString(ARG_TITLE)).setMultiChoiceItems(brands, selected,
                    new DialogInterface.OnMultiChoiceClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which, boolean isChecked) {
                            selected[which] = isChecked;
                        }
                    })
                    .setNegativeButton(android.R.string.cancel, null)
                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            StringBuilder sb = new StringBuilder();
                            for (int i = 0; i < selected.length; i++) {
                                if (selected[i]) {
                                    sb.append(brands[i]).append(" ");
                                }
                            }
    
                            Toast.makeText(getContext(), sb.toString(), Toast.LENGTH_SHORT).show();
    
                        }
    
                    });
    
            return builder.create();
        }
    
        @Override
        public void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
            outState.putBooleanArray(STATE_SELECTED, selected);
        }
    
    }
    

    Desde la Activity vamos mostrar el diálogo del siguiente modo, con los elementos 0 y 3 seleccionados

    findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    DialogMultiplePickerFragment dialogFragment = DialogMultiplePickerFragment.newInstance(brands, getString(R.string.pick_brands), 0, 3);
                    dialogFragment.show(getSupportFragmentManager(), DialogMultiplePickerFragment.class.getSimpleName());
                }
            });
    

    Retorno de parámetros

    Cuando el usuario selecciona algún elemento y pulsa Ok, debemos devolver a la Activity los elementos seleccionados. Este comportamiento puede implementarse siguiendo las dos estrategias que se explican a continuación.

    Llamar a la Activity o al Fragment

    Podemos hacer que el DialogFragment invoque un método de la Activity o el Fragment que lo está mostrando. Para desacoplar el DialogFragment de la Activity, definiremos una interfaz y haremos que sea implementada por la Activity o Fragment.

    
    package com.danielme.android.dialogfragmentpicker;
    
    public interface DialogPickerFragmentListener {
    
        void getSelected(String selected);
    }
    

    Vamos a aplicar esta estrategia, explicada con un ejemplo en la documentación oficial de Android, a nuestro DialogSinglePickerFragment. Eso sí, la documentación no parece estar actualizada en estos momentos, y en lugar del método onAttach(Activity activity) que está deprecated, utilizaré onAttach(Context context). En este último método recibimos la Activity que está mostrando el DialogFragment, y guardamos su referencia para poder llamar al método getSelected al pulsarse el botón OK del diálogo. De este modo tendremos en la Activity la opción seleccionada.

    Estos son los cambios a realizar en el fragment.

    package com.danielme.android.dialogfragmentpicker;
    
    import android.app.Dialog;
    import android.content.Context;
    import android.content.DialogInterface;
    import android.os.Bundle;
    import android.support.v7.app.AlertDialog;
    import android.support.v7.app.AppCompatDialogFragment;
    import android.widget.Toast;
    
    public class DialogSinglePickerFragment extends AppCompatDialogFragment {
    
        private static final int UNSELECTED = -1;
        private static final String ARG_SELECTED = "ARG_SELECTED";
        private static final String ARG_ITEMS = "ARG_ITEMS";
        private static final String ARG_TITLE = "ARG_TITLE";
        private static final String STATE_SELECTED = "STATE_SELECTED";
    
        private DialogPickerFragmentListener listener;
    
        private int selected;
    
        public static DialogSinglePickerFragment newInstance(Integer selection, String[] items, String title) {
            DialogSinglePickerFragment fragment = new DialogSinglePickerFragment();
            Bundle args = new Bundle();
            if (selection != null) {
                args.putInt(ARG_SELECTED, selection);
            }
            args.putStringArray(ARG_ITEMS, items);
            args.putString(ARG_TITLE, title);
            fragment.setArguments(args);
            return fragment;
        }
    
        // Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
        @Override
        public void onAttach(Context context) {
            super.onAttach(context);
            // Verify that the host activity implements the callback interface
            try {
                // Instantiate the NoticeDialogListener so we can send events to the host
                listener = (DialogPickerFragmentListener) context;
            } catch (ClassCastException e) {
                // The activity doesn't implement the interface, throw exception
                throw new ClassCastException(context.toString()
                        + " must implement DialogSinglePickerFragment");
            }
        }
    
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            if (savedInstanceState != null) { //rotation
                selected = savedInstanceState.getInt(STATE_SELECTED, UNSELECTED);
            } else if (getArguments() != null) {
                selected = getArguments().getInt(ARG_SELECTED, UNSELECTED);
            }
    
            final String[] brands = getArguments().getStringArray(ARG_ITEMS);
    
            AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
    
            builder.setTitle(getArguments().getString(ARG_TITLE))
                    .setSingleChoiceItems(brands, selected, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            selected = which;
                        }
                    })
                    .setNegativeButton(android.R.string.cancel, null)
                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            if (selected != UNSELECTED) {
                                listener.getSelected(brands[selected]);
                                //Toast.makeText(getContext(), brands[selected], Toast.LENGTH_SHORT).show();
                            }
                        }
                    });
    
            return builder.create();
        }
    
        @Override
        public void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
            outState.putInt(STATE_SELECTED, selected);
        }
    
    }
    

    En la Activity implementamos esta interfaz, y mostramos el resultado recibido en un TextView.

        @Override
        public void getSelected(String selected) {
            textView.setText(selected);
        }
    

    Si el DialogFragment se muestra desde otro Fragment en lugar de una Activity, no necesitamos sobrescribir el método onAttach. En su lugar, al crear el DialogFragment le pasamos la instancia del Fragment que lo crea con el método setTargetFragment de este modo

     DialogSinglePickerFragment dialogFragment = DialogSinglePickerFragment.newInstance(4, brands, getString(R.string.pick_brand));
     dialogFragment.setTargetFragment(this, 0);
     dialogFragment.show(getSupportFragmentManager(), DialogSinglePickerFragment.class.getSimpleName());
    

    En el DialogFragment, lo recuperamos así:

    ((DialogPickerFragmentListener) getTargetFragment()).getSelected(brands[selected]);
    
    Lanzar un evento

    Una alternativa que permite desacoplar totalmente el DialogFragment de la Activity o Fragment que la contiene es enviar un evento dentro de nuestra aplicación al realizarse la selección. Esta estrategia tiene una ventaja añadida: el evento puede ser atendido por cualquier Activity o Fragment de la aplicación.

    Podemos utilizar el mecanismo de broadcasts locales proporcionado de forma nativa por Android, y que explico en el tutorial Android: mensajes y eventos con Broadcasts, o bien un bus de eventos como EventBus que es lo que suelo hacer en mis proyectos. En este caso, y para mantener el tutorial lo más simple posible, voy a utilizar un broadcast local en el DialogFragment de selección múltiple y para cualquier duda al respecto remito al lector a mencionado tutorial.

    En el Fragment, enviamos el broadcast incluyendo en el Intent la selección.

    package com.danielme.android.dialogfragmentpicker;
    
    import android.app.Dialog;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.os.Bundle;
    import android.support.v4.content.LocalBroadcastManager;
    import android.support.v7.app.AlertDialog;
    import android.support.v7.app.AppCompatDialogFragment;
    import android.widget.Toast;
    
    public class DialogMultiplePickerFragment extends AppCompatDialogFragment {
    
        public static final String BROADCAST_MULTIPICK = "BROADCAST_MULTIPICK";
        public static final String SELECTION = "SELECTION";
    
        private static final String ARG_SELECTED = "ARG_SELECTED";
        private static final String STATE_SELECTED = "STATE_SELECTED";
        private static final String ARG_ITEMS = "ARG_ITEMS";
        private static final String ARG_TITLE = "ARG_TITLE";
    
        private boolean[] selected;
    
        public static DialogMultiplePickerFragment newInstance(String[] items, String title, int... selection) {
            DialogMultiplePickerFragment fragment = new DialogMultiplePickerFragment();
            Bundle args = new Bundle();
            if (selection != null) {
                args.putIntArray(ARG_SELECTED, selection);
            }
            args.putStringArray(ARG_ITEMS, items);
            args.putString(ARG_TITLE, title);
            fragment.setArguments(args);
            return fragment;
        }
    
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final CharSequence[] brands = getArguments().getStringArray(ARG_ITEMS);
    
            if (savedInstanceState != null) { //rotation
                selected = savedInstanceState.getBooleanArray(STATE_SELECTED);
            } else if (getArguments() != null) {
                selected = new boolean[brands.length];
                int[] selection = getArguments().getIntArray(ARG_SELECTED);
                for (int aSelection : selection) {
                    selected[aSelection] = true;
                }
            }
    
            AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
    
            builder.setTitle(getArguments().getString(ARG_TITLE)).setMultiChoiceItems(brands, selected,
                    new DialogInterface.OnMultiChoiceClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which, boolean isChecked) {
                            selected[which] = isChecked;
                        }
                    })
                    .setNegativeButton(android.R.string.cancel, null)
                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            StringBuilder sb = new StringBuilder();
                            for (int i = 0; i < selected.length; i++) {
                                if (selected[i]) {
                                    sb.append(brands[i]).append(" ");
                                }
                            }
    
                            //Toast.makeText(getContext(), sb.toString(), Toast.LENGTH_SHORT).show();
                            Intent intent = new Intent();
                            intent.setAction(BROADCAST_MULTIPICK);
                            intent.putExtra(SELECTION, sb.toString());
                            LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
                        }
    
                    });
    
            return builder.create();
        }
    
        @Override
        public void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
            outState.putBooleanArray(STATE_SELECTED, selected);
        }
    
    }
    

    En la Activity, creamos una clase de tipo BroadcastReceiver y registramos una instancia para que se ejecute cuando el DialogFragment envíe el broadcast. También debemos asegurarnos que el broadcast quede “desregistrado” al destruirse la Activity.

    package com.danielme.android.dialogfragmentpicker;
    
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.content.IntentFilter;
    import android.os.Bundle;
    import android.support.v4.content.LocalBroadcastManager;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.Toolbar;
    import android.view.View;
    import android.widget.TextView;
    
    public class MainActivity extends AppCompatActivity implements DialogPickerFragmentListener {
    
        private TextView textView;
        private MultiPickBroadcastReceiver receiver;
        private String[] brands;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
    
            textView = findViewById(R.id.textView);
    
            brands = new String[]{getString(R.string.htc), getString(R.string.lg), getString(R.string.meizu),
                    getString(R.string.motorola), getString(R.string.samsung)};
    
            setupButtons();
    
            setupBroadcast();
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
        }
    
        private void setupBroadcast() {
            receiver = new MultiPickBroadcastReceiver();
            IntentFilter filter = new IntentFilter();
            filter.addAction(DialogMultiplePickerFragment.BROADCAST_MULTIPICK);
            LocalBroadcastManager.getInstance(this).registerReceiver(receiver, filter);
        }
    
        private void setupButtons() {
            findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    DialogSinglePickerFragment dialogFragment = DialogSinglePickerFragment.newInstance(4, brands, getString(R.string.pick_brand));
                    dialogFragment.show(getSupportFragmentManager(), DialogSinglePickerFragment.class.getSimpleName());
                }
            });
    
            findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    DialogMultiplePickerFragment dialogFragment = DialogMultiplePickerFragment.newInstance(brands, getString(R.string.pick_brands), 0, 3);
                    dialogFragment.show(getSupportFragmentManager(), DialogMultiplePickerFragment.class.getSimpleName());
                }
            });
        }
    
        @Override
        public void getSelected(String selected) {
            textView.setText(selected);
        }
    
        class MultiPickBroadcastReceiver extends BroadcastReceiver {
    
            @Override
            public void onReceive(Context context, Intent intent) {
                textView.setText(intent.getStringExtra(DialogMultiplePickerFragment.SELECTION));
            }
        }
    }
    
    
    Estilos

    En nuestros diálogos de selección no hay mucho que hacer en lo que a estilos respecta: a los radio\checkbox y a los botones se les aplica el accentColor del tema Material Design de la aplicación, y que en nuestro ejemplo vimos al principio del tutorial. Puesto que no hemos definido ningún accentColor, se está aplicando el del tema por defecto que es verde aunque según el fabricante puede haber sido modificado.

    Podemos cambiar el accent color en el tema para toda la aplicación, pero también podemos definir un accent color sólo para los AlertDialog del siguiente modo.

    <?xml version="1.0" encoding="utf-8"?>
    
    <resources xmlns:tools="http://schemas.android.com/tools">
    
        <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
            <item name="colorPrimary">@color/primary</item>
            <item name="colorPrimaryDark">@color/primaryDark</item>
            <item name="alertDialogTheme">@style/AppTheme.AlertDialog</item>
        </style>
    
        <style name="AppTheme.Toolbar">
            <item name="android:layout_width">match_parent</item>
            <item name="android:layout_height">?attr/actionBarSize</item>
            <item name="android:background">@color/primary</item>
            <item name="android:elevation" tools:targetApi="lollipop">4dp</item>
            <item name="android:theme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item>
        </style>
    
        <style name="AppTheme.AlertDialog" parent="Theme.AppCompat.Light.Dialog.Alert">
            <item name="colorAccent">@color/dialogColorAccent</item>
        </style>
    
    </resources>
    

    Código de ejemplo

    El proyecto de ejemplo para Android Studio se encuentra disponible en GitHub. Para más información sobre cómo utilizar GitHub, consultar este artículo.

    Tutoriales relacionados

    1. Tip Android #15: Dialog personalizado
    2. Tip Android #28: ProgressDialog personalizado

    Master Pyhton, Java, Scala or Ruby

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: