Ejercicio de diseño: de LinearLayout a ConstraintLayout

Ejercicio de diseño: de LinearLayout a ConstraintLayout

Hasta ahora hemos visto diversos conceptos relativos al diseño y la distribución de elementos en la pantalla, pero la mejor manera de familiarizarse con ellos es utilizarlos y trastear con el editor de diseño. Por ello, en esta entrada vamos a practicar con el diseño de una aplicación, y concretamente vamos a transformar una aplicación diseñada a base de LinearLayout para convertir el contenedor principal de elementos en un ConstraintLayout (ver Fundamentos de Android: Diseño con ConstraintLayout) y que el diseño sea más limpio reduciendo la jerarquía de elementos anidados. Además vamos utilizar otros conceptos conocidos como cadenas de elementos (chain), elementos de imagen, recursos de distinto tipo (texto, dimensiones, colores, estilos, etc.) y algunos más.

Vamos a partir del proyecto ControlAcceso, que hemos ido retocando en las últimas entradas, tal como lo dejamos en Data Binding para mostrar datos. Concretamente, en esa entrada el código de los archivos de diseño quedó así:
<!--- activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools">

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            tools:context=".MainActivity">

        <LinearLayout
                android:id="@+id/linearLayout"
                style="@style/estilo_linear"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

            <TextView
                    android:id="@+id/user_txt"
                    style="@style/estilo_texto"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginEnd="@dimen/margen_txt_end1"
                    android:text="@string/usuario" />

            <EditText
                    android:id="@+id/user_input"
                    style="@style/estilo_texto"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:ems="10"
                    android:hint="@string/usuario"
                    android:inputType="textPersonName" />
        </LinearLayout>

        <LinearLayout
                android:id="@+id/linearLayout2"
                style="@style/estilo_linear"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center_horizontal"
                android:orientation="horizontal">

            <TextView
                    android:id="@+id/pass_txt"
                    style="@style/estilo_texto"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginEnd="@dimen/margen_txt_end2"
                    android:text="@string/contraseña" />

            <EditText
                    android:id="@+id/pass_input"
                    style="@style/estilo_texto"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:ems="10"
                    android:hint="@string/contraseña"
                    android:inputType="textPassword" />
        </LinearLayout>

        <Button
                android:id="@+id/entrar_btn"
                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/margen_linea"
                android:text="@string/btn_entrar" />

    </LinearLayout>

</layout>
<!--- activity_login_check.xml -->
<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
                name="usuario"
                type="com.android.controlacceso.Usuario" />
    </data>

    <LinearLayout
            style="@style/estilo_linear"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            tools:context=".LoginCheck">

        <TextView
                android:id="@+id/acceso_txt"
                style="@style/estilo_texto"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/margen_linea"
                android:text="@string/login_check"
                android:textAlignment="center" />

        <TextView
                android:id="@+id/login_txt"
                style="@style/estilo_texto"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/margen_linea"
                android:text="@={usuario.nombre}"
                android:textAlignment="center" />

        <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

            <EditText
                    android:id="@+id/pass_txt"
                    style="@style/estilo_texto"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:background="@null"
                    android:ems="10"
                    android:enabled="false"
                    android:inputType="textPassword"
                    android:text="@={usuario.pass}"
                    android:textAlignment="center" />

            <CheckBox
                    android:id="@+id/checkBox"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:text="@string/btn_ver" />

        </LinearLayout>

        <Button
                android:id="@+id/button_cerrar"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/margen_linea"
                android:text="@string/cerrar" />

    </LinearLayout>

</layout>

En las siguientes imágenes se aprecia el editor de diseño de ambos archivos después de la conversión del LinearLayout a un ConstraintLayout, observándose en el árbol de componentes los elementos de diseño utilizados, y finalmente unas capturas de pantalla de la aplicación en funcionamiento:






Como se supone que ya se conocen los métodos de aplicación de las técnicas utilizadas (en caso contrario revisar las entradas correspondientes), solo se expone el resultado final del código:
<!--- activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools">

    <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/constraint_layout"
            style="@style/estilo_constraint"
            tools:context=".MainActivity">

        <TextView
                android:id="@+id/textView"
                style="@style/estilo_titulo"
                android:text="@string/acceso"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

        <ImageView
                android:id="@+id/imageView"
                style="@style/estilo_imagen"
                android:contentDescription="@string/imagen_candado"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/textView"
                app:srcCompat="@android:drawable/ic_secure" />

        <TextView
                android:id="@+id/user_txt"
                style="@style/estilo_texto"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="@dimen/margen16"
                android:text="@string/usuario"
                app:layout_constraintBaseline_toBaselineOf="@+id/user_input"
                app:layout_constraintEnd_toStartOf="@+id/user_input"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintHorizontal_chainStyle="packed"
                app:layout_constraintStart_toStartOf="parent" />

        <EditText
                android:id="@+id/user_input"
                style="@style/estilo_texto"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/margen32"
                android:layout_marginEnd="@dimen/margen16"
                android:ems="10"
                android:hint="@string/usuario"
                android:inputType="textPersonName"
                android:textAlignment="textEnd"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toEndOf="@+id/user_txt"
                app:layout_constraintTop_toBottomOf="@+id/imageView" />

        <TextView
                android:id="@+id/pass_txt"
                style="@style/estilo_texto"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="@dimen/margen16"
                android:text="@string/contraseña"
                app:layout_constraintBaseline_toBaselineOf="@+id/pass_input"
                app:layout_constraintEnd_toStartOf="@+id/pass_input"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintStart_toStartOf="parent" />

        <EditText
                android:id="@+id/pass_input"
                style="@style/estilo_texto"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/margen32"
                android:layout_marginEnd="@dimen/margen16"
                android:ems="10"
                android:hint="@string/contraseña"
                android:inputType="textPassword"
                android:textAlignment="textEnd"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toEndOf="@+id/pass_txt"
                app:layout_constraintTop_toBottomOf="@+id/user_input" />

        <Button
                android:id="@+id/entrar_btn"
                style="@style/Widget.AppCompat.Button.Colored"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:layout_marginBottom="@dimen/margen32"
                android:text="@string/sesion_inicio"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>
<!--- activity_login_check.xml -->
<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
                name="usuario"
                type="com.android.controlacceso.Usuario" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/constraint_layout"
            style="@style/estilo_constraint"
            android:background="@color/fondo"
            android:padding="@dimen/padding16"
            tools:context=".LoginCheck">

        <TextView
                android:id="@+id/acceso_txt"
                style="@style/estilo_titulo"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/login_check"
                android:textAlignment="center"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

        <ImageView
                android:id="@+id/imageView2"
                style="@style/estilo_imagen"
                android:contentDescription="@string/imagen_candado_abierto"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/acceso_txt"
                app:srcCompat="@android:drawable/ic_partial_secure" />

        <TextView
                android:id="@+id/login_txt"
                style="@style/estilo_texto"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="@dimen/margen32"
                android:layout_marginTop="@dimen/margen_linea"
                android:layout_marginEnd="@dimen/margen32"
                android:text="@={usuario.nombre}"
                android:textAlignment="center"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/imageView2" />


        <EditText
                android:id="@+id/pass_txt"
                style="@style/estilo_texto"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="@dimen/margen32"
                android:layout_weight="1"
                android:background="@null"
                android:ems="10"
                android:enabled="false"
                android:hint="@string/code_pass"
                android:inputType="textPassword"
                android:text="@={usuario.pass}"
                android:textAlignment="center"
                app:layout_constraintBaseline_toBaselineOf="@+id/checkBox"
                app:layout_constraintEnd_toStartOf="@+id/checkBox"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintStart_toStartOf="parent" />

        <CheckBox
                android:id="@+id/checkBox"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/margen32"
                android:layout_marginEnd="@dimen/margen8"
                android:layout_weight="1"
                android:text="@string/btn_ver"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintStart_toEndOf="@+id/pass_txt"
                app:layout_constraintTop_toBottomOf="@+id/login_txt" />

        <Button
                android:id="@+id/button_cerrar"
                style="@style/Widget.AppCompat.Button.Colored"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="@dimen/margen32"
                android:layout_marginEnd="@dimen/margen32"
                android:layout_marginBottom="@dimen/margen32"
                android:text="@string/cerrar"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

Por otra parte, hemos aprovechado estos cambios de diseño para mejorar el código del controlador del CheckBox en el archivo de la actividad LoginCheck, que estaba de esta manera:
binding.checkBox.setOnClickListener(View.OnClickListener {
    if (binding.checkBox.isChecked) {
        binding.passTxt.inputType = InputType.TYPE_CLASS_TEXT
    } else {
        binding.passTxt.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
    }
})
para dejarlo así (lo que hace innecesaria la instrucción de importación import android.view.View):
binding.checkBox.setOnClickListener {
    if (binding.checkBox.isChecked) {
        binding.passTxt.inputType = InputType.TYPE_CLASS_TEXT
    } else {
        binding.passTxt.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
    }
}

Comentarios

Entradas populares

I/O: entrada y salida de datos en consola

Recursos gratis para aprender Kotlin

Lectura y escritura de archivos