# Sesión 2 - Interfaz de usuario

En esta sesión vamos a introducir el diseño y programación de interfaces de usuario básicas para Android. La API de Android proporciona el acceso a una serie de componentes de alto nivel que nos ahorran tener que programarlos desde cero. Por otro lado su uso nos permitirá dar a nuestras aplicaciones el mismo aspecto que el resto de aplicaciones del sistema.

## Views

Todos los componentes visuales de la interfaz de usuario, tales como botones, campos de texto, selectores, etc, se denominan `View`s en Android. Los views se pueden agrupar en `ViewGroup`s que sirven para reutilizar componentes que siempre vayan a utilizarse juntos.

Los views se distribuyen sobre `Layout`s. Hay distintos tipos de layout, según la distribución de componentes que queramos tener en la pantalla. El layout que más se utiliza es el `LinearLayout` que puede disponer los componentes uno después del otro, o bien horizontalmente, o bien verticalmente. Para hacer combinaciones se pueden incluir layouts más pequeños dentro de otros.

Cualquier view o layout puede ocupar, o bien el tamaño completo que su contenedor le permita: `fill_parent`, o bien el tamaño mínimo que necesite para dar cabida a los componentes y contenidos que haya en él: `wrap_content`. Estos dos valores pueden ser aplicados tanto en horizontal como en vertical, mediante los atributos `layout_width` y `layout_height`.

Aunque cualquier interfaz gráfico se podría crear programáticamente, sin hacer uso de ningún recurso XML, lo normal es diseñar nuestros layouts en formato XML y con la ayuda del diseñador de interfaces disponible en el plugin de Android para Eclipse. Así, podemos introducir un componente `TextView` en un layout llamado `main.xml` y mostrarlo en nuestra actividad principal:

```java
public class Interfaces extends Activity 
{
    /** 
    * Called when the activity is first created. 
    */
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ((TextView)findViewByID(R.id.TextView01)).setText("Hola Android");
    }
}
```

donde el XML de `main.xml` sería:

```markup
<LinearLayout
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content">
        <TextView 
        android:text="Hola Android" 
        android:id="@+id/TextView01" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" />
</LinearLayout>
```

O bien podemos prescindir de recursos XML y añadir los views desde el código:

```java
@Override
public void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);

    TextView miTextView = new TextView(this);
    setContentView(miTextView);
    miTextView.setText("Hola Android");
}
```

Por supuesto, es preferible trabajar con los recursos XML cuando sea posible, ya que facilitan la mantenibilidad del programa, así como su diseño, que viene apoyado por la herramienta diseñadora del plugin para Eclipse.

Algunos views estándar que Android proporciona son los siguientes:

* `TextView`, etiqueta de texto.&#x20;
* `EditText`, campo de texto.
* `Button`, botón pulsable con etiqueta de texto.
* `ListView`, grupo de views que los visualiza en forma de lista vertical.
* `Spinner`, lista desplegable, internamente es una composición de TextView y de List View.
* `CheckBox`, casilla marcable de dos estados.&#x20;
* `RadioButton`, casilla seleccionable de dos estados, donde un grupo de RadioButtons sólo permitiría seleccionar uno de ellos al mismo tiempo.
* `ViewFlipper`, un grupo de Views que nos permite seleccionar qué view visualizar en este momento.&#x20;
* `ScrollView`, permite usar barras de desplazamiento. Sólo puede contener

  un elemento, que puede ser un Layout (con otros muchos elementos dentro).
* `DatePicker`, permite escoger una fecha.
* `TimePicker`, permite escoger una hora.
* Otros más avanzados como&#x20;

  `MapView` (vista de Google Maps) y `WebView` (vista de navegador web), etc.

Una buena práctica de programación es extender el comportamiento de los componentes por medio de herencia. Así crearemos nuestros propios componentes personalizados. Más información sobre Views se puede obtener en el tutorial oficial: <http://developer.android.com/resources/tutorials/views/index.html>.

## Algunas clases útiles

En la API para interfaces gráficos hay otras clases útiles para la interacción con el usuario. Veamos algunas de ellas.

### Toast

Los `Toast` sirven para mostrar al usuario algún tipo de información de la manera menos intrusiva posible, sin robar el foco a la actividad y sin pedir ningún tipo de interacción, desapareciendo automáticamente. El tiempo que permanecerá en pantalla puede ser, o bien `Toast.LENGTH_SHORT`, o bien `Toast.LENGTH_LONG`. Se crean y muestran así:

```java
Toast.makeText(MiActividad.this, 
"Preferencia de validación actualizada",
Toast.LENGTH_SHORT).show();
```

No hay que abusar de ellos pues, si se acumulan varios, irán apareciendo uno después de otro, esperando a que acabe el anterior y quizás a destiempo. Son útiles para confirmar algún tipo de información al usuario, que le dará seguridad de que está haciendo lo correcto. No son útiles para mostrar información importante, ni información extensa. Por último, si se van a utilizar como mensajes de Debug, aunque son útiles es mucho mejor utilizar la instrucción `LOG.d("TAG","Mensaje a mostrar")` y seguir el LogCat en el nivel de debugging.

### AlertDialog

Los `AlertDialog` son útiles para pedir confirmaciones, o bien formular preguntas que requieran pulsar "aceptar" o "cancelar". A continuación se muestra un ejemplo de la documentación oficial de Android:

```java
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Are you sure you want to exit?")
       .setCancelable(false)
       .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
                MyActivity.this.finish();
           }
       })
       .setNegativeButton("No", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
                dialog.cancel();
           }
       });
AlertDialog alert = builder.create();
```

![](https://888516061-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LwrBJxp0xTANGM036-G%2F-LwrBLLK_CMs3zo9_Yuv%2F-LwrBUq5p9EQRxdBn-wL%2Fdialog_buttons.png?generation=1577179057644147\&alt=media)

Si queremos una lista de la que seleccionar, podemos conseguirlo de la siguiente manera:

```java
final CharSequence[] items = {"Red", "Green", "Blue"};

AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Pick a color");
builder.setItems(items, new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int item) {
        Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show();
    }
});
AlertDialog alert = builder.create();
```

![](https://888516061-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LwrBJxp0xTANGM036-G%2F-LwrBLLK_CMs3zo9_Yuv%2F-LwrBUq7DBpdmKihu-Cw%2Fdialog_list.png?generation=1577179053235856\&alt=media)

### ProgressDialog

Los `ProgressDialog` sirven para indicar progreso. Por ejemplo:

```java
ProgressDialog dialog = ProgressDialog.show(MyActivity.this, "", 
        "Loading. Please wait...", true);
```

genera un diálogo con indicador de progreso indefinido:

![](https://888516061-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LwrBJxp0xTANGM036-G%2F-LwrBLLK_CMs3zo9_Yuv%2F-LwrBUq9pgi9dg6pISn4%2Fdialog_progress_spinning.png?generation=1577179053104254\&alt=media)

mientras que

```java
ProgressDialog progressDialog;
progressDialog = new ProgressDialog(mContext);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage("Loading...");
progressDialog.setCancelable(false);
```

genera un diálogo con una barra de progreso horizontal:

![](https://888516061-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LwrBJxp0xTANGM036-G%2F-LwrBLLK_CMs3zo9_Yuv%2F-LwrBUqBPooHAA1bv5v2%2Fdialog_progress_bar.png?generation=1577179052909303\&alt=media)

En este caso, para indicar el progreso se utiliza el método `progressDialog.setProgress(int)` indicando el porcentaje total de progreso, y lo típico sería que esta llamada se hiciera desde otro hilo con un `Handler`, o bien desde el método `AsyncTask.onProgressUpdate(String)`.

### InputFilter

Cuando se introduce texto en un `EditText`, el contenido permitido se puede limitar y/o corregir usando un `InputFilter` o una colección de ellos. Hay dos `InputFilter` ya creados, uno es para obligar a que todo sean mayúsculas y el otro es para limitar la longitud del campo. Además se pueden crear filtros personalizados. En el siguiente ejemplo se asignan tres filtros (uno de cada tipo) a un campo de texto. Los filtros se aplican por el orden en el que estén en el vector.

```java
EditText editText = (EditText)findViewById(R.id.EditText01);
InputFilter[] filters = new InputFilter[3];
filters[0] = new InputFilter.LengthFilter(9);
filters[1] = new InputFilter.AllCaps();
filters[2] = new InputFilter() {
    public CharSequence filter(CharSequence source, int start, int end, 
        Spanned dest, int dstart, int dend) {
    if (end > start) {
        String destTxt = dest.toString();
        String resultingTxt = destTxt.substring(0, dstart) + 
            source.subSequence(start, end) + destTxt.substring(dend);
        if (!resultingTxt.matches("^[A-F0-9]*$")) {
         if (source instanceof Spanned) {
             SpannableString sp = new SpannableString("");
             return sp;
         } else {
             return "";
         }
        }
    }
    return null;
    }
};
dniEditText.setFilters(filters);
```

El último filtro comprueba que se cumpla la expresión regular `^[A-F0-9]*$` (caracteres de número hexadecimal).

## Layouts

Los Layouts son una extensión de la clase `ViewGroup` y se utilizan para posicionar controles (Views) en la interfaz de usuario. Se pueden anidar unos dentro de otros.

Los layout se pueden definir en formato XML en la carpeta `res/layout`. Por ejemplo, el siguiente layout lineal dispondrá sus elementos (TextView y Button) uno debajo del otro:

```markup
<LinearLayout
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="right">

    <TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello" />

    <Button android:text="Siguiente" 
    android:id="@+id/Button01" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" />

</LinearLayout>
```

Para disponerlos uno al lado del otro se utiliza `orientation="horizontal"`. Por otro lado, el atributo `gravity` indica hacia qué lado se van a alinear los componentes.

Algunos de los layouts más utilizados son:

* `LinearLayout`, dispone los elementos uno después del otro.
* `FrameLayout`, dispone cualquier elemento en la esquina superior izquierda.
* `RelativeLayout`, dispone los elementos en posiciones relativas con respecto a otros, y con respecto a las fronteras del layout.
* `TableLayout`, dispone los elementos en forma de filas y columnas.
* `Gallery`, dispone los elementos en una única fila desplazable.

## Eventos

Para que los views sean usables, hay que asignar manejadores a los eventos que nos interesen. Por ejemplo, para un `Button` podemos asociar un comportamiento asignándole un `onClickListener`:

```java
ImageButton imageButton = (ImageButton)findViewById(R.id.ImageButton01);
imageButton.setOnClickListener(new OnClickListener() 
{
    @Override
    public void onClick(View v) {
        Toast.makeText(getApplicationContext(), 
         "Gracias por pulsar.", Toast.LENGTH_SHORT).show();

    }
});
```

Se pueden escuchar los eventos de cualquier otro tipo de view, incluso de los `TextView`.

## Activities e Intents

Ya estamos familiarizados con las actividades de android: se trata de tareas que muestran un interfaz gráfico al usuario, y sólo podemos ver en pantalla una Activity a la vez. Muchas aplicaciones tienen una actividad principal que puede llevarnos a otras actividades de la aplicación, o incluso a actividades de otras aplicaciones.

![](https://888516061-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LwrBJxp0xTANGM036-G%2F-LwrBLLK_CMs3zo9_Yuv%2F-LwrBUqDnn09nR3lFBwT%2Factivity_lifecycle.png?generation=1577179052837715\&alt=media)

Este ciclo de vida puede definirse por medio de los siguientes métodos:

```java
public class Activity extends ApplicationContext 
{
     protected void onCreate(Bundle savedInstanceState);

     protected void onStart();

     protected void onRestart();

     protected void onResume();

     protected void onPause();

     protected void onStop();

     protected void onDestroy();
}
```

Hay tres subciclos típicos en la vida de una actividad:

* Todo el ciclo de vida ocurre desde la primera llamada a `onCreate(Bundle)` hasta la llamada (única) al método `onDestroy()`. Por tanto en esta última se deben liberar los recursos que queden por liberar.
* El tiempo de vida visible ocurre entre `onStart()` y `onStop()`. Durante este tiempo el usuario verá la actividad en pantalla, incluso aunque ésta no tenga el foco en este momento o esté en segundo plano (pero visible). Los métodos `onStart()` y `onStop()` podrán ser llamados múltiples veces, según la actividad se haga visible o se oculte.
* El tiempo de vida en primer plano ocurre entre los métodos `onResume` y `onPause`. La actividad puede pasar con frecuencia entre el estado pausado y primer plano, de manera que el código de estos métodos debe ser rápido.

> Cuando una actividad se pausa, ésta puede no volver nunca a primer plano sino ser matada debido a que el sistema operativo lo decida así, por falta de recursos de memoria. Por tanto tendremos que intentar guardar los estados de nuestras actividades de tal manera que si la actividad se retoma con `onResume()`, el usuario tenga la misma experiencia que si arranca la aplicación de nuevo. Para ello nos ayuda el parámetro de `onCreate(Bundle)` que guarda el estado de los formularios y los rellena de nuevo "automágicamente", sin tener que programarlo nosotros.

Para pasar de una actividad a otra se utilizan los `Intent`. Un Intent es una descripción abstracta de una operación a realizar. Se puede utilizar con el método `startActivity` para lanzar una actividad, con `broadcastIntent` para enviarse a cualquier componente receptor `BroadcastReceiver`, y con `startService` o `bindService` para comunicar con un servicio (`Service`) que corre en segundo plano.

Por ejemplo, para lanzar la actividad llamada `MiActividad`, lo haremos así:

```java
Intent intent = new Intent(this, MiActividad.class);
startActivity(intent);
```

Para iniciar una llamada de teléfono también utilizaremos intents:

```java
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:965903400"));
startActivity(intent);
```

Podemos pasar un código de petición a la actividad:

```java
startActivityForResult(intent, CODIGO_INT);
```

Y podemos esperar un resultado de la actividad, generándolo así

```java
Intent resultado = new Intent(null, Uri.parse("content://datos/1");
resultado.putExtra(1,"aaa");
resultado.putExtra(2,"bbb");

if(ok)
    setResult(RESULT_OK, resultado);
else
    setResult(RESULT_CANCELED, null);

finish();
```

y recogiendo el resultado con el método `onActivityResult()` sobrecargado:

```java
@Override
public void onActivityResult(int reqCode, int resultCode, Intent data){
    switch(reqCode){
    case 1: 
        break;
    case 2:
        break;
    default:
    }
}
```

Algunas acciones nativas da Android son:

* ACTION\_ANSWER
* ACTION\_CALL
* ACTION\_DELETE
* ACTION\_DIAL
* ACTION\_EDIT
* ACTION\_INSERT
* ACTION\_PICK
* ACTION\_SEARCH
* ACTION\_SENDTO
* ACTION\_VIEW
* ACTION\_WEB\_SEARCH


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ajgallego.gitbook.io/introduccion-a-android/sesion02-apuntes.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
