Fundamentos de Android: Interacción del usuario

Fundamentos de Android: Interacción del usuario

En esta entrada vamos a practicar agregando cierta interacción del usuario a una aplicación, y para ello vamos a retomar la aplicación About que diseñamos en la entrada Fundamentos de Android: Editor gráfico de diseño. Concretamente vamos a permitir al usuario introducir un texto y pulsar un botón para actualizar un elemento visual con ese texto.

Bien, pues vamos a la tarea. Abrimos el proyecto About con Android Studio, que quedó más o menos así:



Para que el usuario ingrese su texto primero necesitamos añadir un campo de edición de texto (EditText, que es una subclase del TextView que ya conocemos). Para ello, en el archivo activity_main.xml, vista Design, desde la paleta de elementos seleccionamos Text y nos muestra distintos elementos disponibles. Observa que los que empiezan con Ab subrayado indican que el contenido es editable por el usuario, y por tanto son EditText, aunque cada uno con distintos atributos.

Nosotros vamos a utilizar un EditText sin formato (Plain Text) y lo arrastramos al árbol de componentes entre el TextView y la ImageView que contiene la estrella:



Ahora utilizamos el panel de Atributos para configurar este nuevo EditText de esta manera:
ID: usuario
layout_width: match_parent
layout_height: wrap_content

Después le cambiamos el texto por defecto que sirve como sugerencia al usuario acerca del tipo de texto que se espera que introduzca en un EditText (por ejemplo, cosas como Usuario, Email, Teléfono...). Para eso desde el panel de Atributos, en el campo text eliminamos el texto por defecto (si no se elimina no se muestra la sugerencia) y en el campo hint escribimos: ¿Nombre de Usuario? (recuerda añadir el recurso de texto en el archivo values/strings.xml, algo así como: <string name="nombre_usuario">¿Nombre de Usuario?</string> y en hint: @string/nombre_usuario).

Además, en el campo style le aplicamos el estilo que ya tenemos creado (AppEstilo) y lo centramos (textAlignment: center). Después ajustamos el atributo inputType a textPersonName. Este atributo especifica el tipo de entrada que el usuario ingresará y, en consecuencia, el tipo de teclado que se mostrará en pantalla; por ejemplo si se usa el valor phone aparece un teclado numérico y si se usa textPassword el texto introducido no será visible.

Una vez que ya tenemos el campo de texto donde el usuario puede introducir un texto, desde la paleta de elementos agregamos un botón al panel del árbol de componentes, bajo el EditText:



Creamos un nuevo recurso de texto en values/strings.xml, algo así como:
<string name="ok">OK</string>

Y ajustamos los atributos del botón:
ID: ok_boton
text: @string/ok
layout_gravity: center_horizontal (centra el elemento respecto al elemento padre, que es LinearLayout)
layout_width: wrap_content

Además cambiamos el estilo (style) a Widget.AppCompat.Button.Colored que es uno de los estilos predefinidos que ofrece Android y que podemos seleccionar tanto desde el menú desplegable de style o desde la ventana Resources con el botón de tres puntitos:



Este estilo cambia el color del botón al color de énfasis del tema (colorAccent) que está definido en el archivo values/colors.xml: <color name="colorAccent">#D81B60</color>. Este archivo contiene los colores predeterminados para la aplicación, pudiéndose agregar nuevos recursos de color o cambiar los recursos de color existentes según el aspecto que necesitemos para la aplicación. Por ejemplo, para que utilice un color más similar al color de la barra de aplicación podemos cambiarlo a <color name="colorAccent">#009f8e</color>.

También le podemos agregar un margen superior al botón con el atributo Layout_Margin -> Top y el valor layout_margin que ya teníamos definido como un recurso en values/dimens.xml. Y en el desplegable del atributo fontFamily seleccionamos la fuente roboto (@font/roboto).

Cambiando a la pestaña Text podemos verificar el código XML generado para este botón:
<Button
    android:id="@+id/button"
    style="@style/Widget.AppCompat.Button.Colored"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:layout_marginTop="@dimen/layout_margin"
    android:fontFamily="@font/roboto"
    android:text="@string/ok" />

Ahora nos falta añadir un elemento que muestre, después de pulsar el botón, el texto escrito por el usuario. Para ello arrastramos un TextView a la paleta de componentes, debajo del botón y le aplicamos estos atributos:
ID: usuario_input
style: @style/AppEstilo
textAlignment: center

Después debemos ajustar su visibilidad con el atributo visibility porque no queremos que se muestre cuando cuando todavía el usuario no ha introducido su texto. Este atributo admite cuatro valores: none, visible (siempre visible), invisible (oculta pero ocupa espacio) y gone (oculta y sin ocupar espacio en el diseño). Seleccionamos gone y eliminamos el valor por defecto del atributo text.
<TextView
    android:id="@+id/usuario_input"
    style="@style/AppEstilo"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAlignment="center"
    android:visibility="gone" />



Ahora nos falta especificar una acción al pulsar el botón, que hacemos con una función implementada en el archivo de la Actividad para manejar el evento de clic sobre el botón que hace de controlador o detector de clics.

Básicamente podemos adjuntar esta función de dos maneras:
  1. En el diseño XML agregamos el atributo android:onClick al elemento Button: android:onClick="clicOk".
  2. En el archivo de la actividad, en onCreate() llamando a setOnClickListener, por ejemplo: button.setOnClickListener { clicOk(it) }.

En este caso vamos a utilizar esta segunda opción, creando una función en el archivo MainActivity.kt que obtenga el texto introducido por el usuario y lo muestre en el TextView que hemos llamado usuario_input a la vez que oculta el campo de edición de texto y el botón.

Abrimos MainActivity.kt y escribimos una función llamada clicOk con un parámetro de tipo View que es el elemento (el botón) que llama a la función.
private fun clicOk(view: View) { }

Para comprobar que el controlador funciona podemos añadir algún contenido de prueba a la función, y tenemos el archivo MainActivity así:
package com.android.about

import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        button.setOnClickListener {
            clicOk(it)
        }
    }

    private fun clicOk(view: View) {
        Toast.makeText(
            this, "Botón pulsado",
            Toast.LENGTH_SHORT
        ).show()
    }
}

O si prefieres un mensaje registrado en el monitor de logcat, en la función clicOk puedes escribir algo así:
Log.i("CONTROL", "Botón pulsado")

Una vez que tenemos bajo control el pulsador del botón, le añadimos el código que realmente nos interesa:
private fun clicOk(view: View) {
    usuario_input.text = usuario.text
    usuario.visibility = View.GONE
    view.visibility = View.GONE
    usuario_input.visibility = View.VISIBLE
}

Ejecutamos en el emulador y comprobamos que funciona. No obstante, observamos que después de pulsar el botón, el teclado aún está visible, puesto que es su comportamiento por defecto. Para cambiarlo (y ocultar el teclado), la función clicOk quedaría así:
private fun clicOk(view: View) {
    usuario_input.text = usuario.text
    usuario.visibility = View.GONE
    view.visibility = View.GONE
    usuario_input.visibility = View.VISIBLE

    val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
    inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
}

Ahora vamos a facilitar que el usuario pueda cambiar el texto después de pulsar el botón. Para ello debemos agregar un detector de clics al TextView que muestra el texto cuya función ocultará el texto y mostrará nuevamente el campo de edición de texto y el botón Ok.

En MainActivity.kt, en la función onCreate(), llamamos a setOnClickListener en el elemento de texto con la función que se activará:
usuario_input.setOnClickListener {
    updateUser(it)
}

Entonces escribimos la función que se activará cuando se pulse sobre sobre el texto:
private fun updateUser (view: View) { }

Y escribimos el bloque de código de la función:
usuario.visibility = View.VISIBLE
button.visibility = View.VISIBLE
view.visibility = View.GONE

Ejecutamos en el emulador de Android para comprobar que funciona. Pero para un usuario podría resultar difícil darse cuenta que puede pulsar sobre el texto para cambiarlo, y para resolverlo podríamos agregar el foco al campo de edición de texto con requestFocus() y mostrar el teclado desde la función updateUser:
usuario.requestFocus()
// mostrar el teclado
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(usuario, 0)

Quedando el archivo MainActivity.kt así:
package com.android.about

import android.content.Context
import android.os.Bundle
import android.view.View
import android.view.inputmethod.InputMethodManager
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        button.setOnClickListener {
            clicOk(it)
        }

        usuario_input.setOnClickListener {
            updateUser(it)
        }
    }

    private fun clicOk(view: View) {
        usuario_input.text = usuario.text
        usuario.visibility = View.GONE
        view.visibility = View.GONE
        usuario_input.visibility = View.VISIBLE

        val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
    }

    private fun updateUser(view: View) {
        usuario.visibility = View.VISIBLE
        button.visibility = View.VISIBLE
        view.visibility = View.GONE
        usuario.requestFocus()
        // mostrar el teclado
        val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        imm.showSoftInput(usuario, 0)
    }
}

Además podríamos ajustar el estilo del texto para que fácilmente se entienda que es un elemento con el que se puede interaccionar. Por ejemplo, le podemos cambiar el color de fondo y el padding al TextView:
<TextView
    android:id="@+id/usuario_input"
    style="@style/AppEstilo"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/colorAccent"
    android:paddingBottom="@dimen/small_padding"
    android:textAlignment="center"
    android:visibility="gone" />

Ejecutamos y ¡listo!

Comentarios

Entradas populares

I/O: entrada y salida de datos en consola

Recursos gratis para aprender Kotlin

Lectura y escritura de archivos