Creación de un juego 3-en-raya (tic-tac-toe, el gato) para dos jugadores locales, que permite la introducción de los nombres de los jugares y cuente con un historial de partidas jugadas.

Este video fue grabado en vivo del curso básico de desarrollo de aplicaciones Android el 2016. Algunas cosas pueden haber cambiado y/o desarrolladas de la forma más sencilla posible, aún así, es buena referencia para quienes que inician en Android y/o programación.

Aprenderás

  • Crear tu primera aplicación Android en Android Studio
  • Usar recursos Material Design, colores recomendados, etc.
  • Crear un adaptador para un RecyclerView

Colores

Colores para el texto

Colores usados en el vídeo:

<color name="colorPrimaryText">#DE000000</color>
<color name="colorSecondaryText">#8A000000</color>
<color name="colorDisabledText">#61000000</color>
    
<color name="colorPrimaryDarkText">#FFFFFF</color>
<color name="colorSecondaryDarkText">#B3FFFFFF</color>
<color name="colorDisabledDarkText">#80FFFFFF</color>

Colores del tema

Colores usados en el video:

<color name="colorPrimary">#00BCD4</color>
<color name="colorPrimaryDark">#0097A7</color>
<color name="colorAccent">#FFD740</color>

Dependencias

Minuto 0:52

Para usar componentes Material Design.

compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:design:23.4.0'
compile 'com.android.support:cardview-v7:23.4.0'
compile 'com.android.support:recyclerview-v7:23.4.0'

Diseño

Minuto 1:05

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="tech.alvarez.tresenraya.MainActivity">

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

        <EditText
            android:id="@+id/jugador1EditText"
            android:layout_width="0dp"
            android:layout_height="48dp"
            android:layout_weight="1"
            android:hint="@string/jugador1" />

        <EditText
            android:id="@+id/jugador2EditText"
            android:layout_width="0dp"
            android:layout_height="48dp"
            android:layout_weight="1"
            android:hint="@string/jugador2" />

    </LinearLayout>

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

        <Button
            android:id="@+id/unoButton"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:onClick="presionar1"
            android:textSize="50sp" />

        <Button
            android:id="@+id/dosButton"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:onClick="presionar2"
            android:textSize="50sp" />

        <Button
            android:id="@+id/tresButton"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:onClick="presionar3"
            android:textSize="50sp" />

    </LinearLayout>

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

        <Button
            android:id="@+id/cuatroButton"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:onClick="presionar4"
            android:textSize="50sp" />

        <Button
            android:id="@+id/cincoButton"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:onClick="presionar5"
            android:textSize="50sp" />

        <Button
            android:id="@+id/seisButton"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:onClick="presionar6"
            android:textSize="50sp" />

    </LinearLayout>

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

        <Button
            android:id="@+id/sieteButton"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:onClick="presionar7"
            android:textSize="50sp" />

        <Button
            android:id="@+id/ochoButton"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:onClick="presionar8"
            android:textSize="50sp" />

        <Button
            android:id="@+id/nueveButton"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:onClick="presionar9"
            android:textSize="50sp" />

    </LinearLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/historialRecyclerView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

</LinearLayout>

Si bien se podría usar otros componentes como GridView en lugar de varios LinearLayouts.

Activity

Minuto 5:09

Referencias

private EditText jugador1EdiText;
private EditText jugador2EdiText;

private Button unoButton;
private Button dosButton;
private Button tresButton;

private Button cuatroButton;
private Button cincoButton;
private Button seisButton;

private Button sieteButton;
private Button ochoButton;
private Button nueveButton;

private RecyclerView historialRecyclerView;

onCreate()

jugador1EdiText = (EditText) findViewById(R.id.jugador1EditText);
jugador2EdiText = (EditText) findViewById(R.id.jugador2EditText);

unoButton = (Button) findViewById(R.id.unoButton);
dosButton = (Button) findViewById(R.id.dosButton);
tresButton = (Button) findViewById(R.id.tresButton);
cuatroButton = (Button) findViewById(R.id.cuatroButton);
cincoButton = (Button) findViewById(R.id.cincoButton);
seisButton = (Button) findViewById(R.id.seisButton);
sieteButton = (Button) findViewById(R.id.sieteButton);
ochoButton = (Button) findViewById(R.id.ochoButton);
nueveButton = (Button) findViewById(R.id.nueveButton);

historialRecyclerView = (RecyclerView) findViewById(R.id.historialRecyclerView);

onClick para los botones

La acción para los botones se generan automáticamente presionando Alt+Enter y escoger la generación del método.

Acciones

Minuto 8:21

Variable global para detectar si toca X

private boolean tocaX = true;

Método de un botón

if (unoButton.getText().toString().equals("")) {
    if (tocaX) {
        unoButton.setText("X");
    } else {
        unoButton.setText("O");
    }
    tocaX = !tocaX;
}

Método gano

Minuto 10:53

public boolean gano(String simbolo) {

    boolean siHayGanador = false;

    if (unoButton.getText().equals(simbolo) && dosButton.getText().equals(simbolo) && tresButton.getText().equals(simbolo)) {
        siHayGanador = true;
    }
    if (unoButton.getText().equals(simbolo) && cuatroButton.getText().equals(simbolo) && sieteButton.getText().equals(simbolo)) {
        siHayGanador = true;
    }
    if (unoButton.getText().equals(simbolo) && cincoButton.getText().equals(simbolo) && nueveButton.getText().equals(simbolo)) {
        siHayGanador = true;
    }

    if (cuatroButton.getText().equals(simbolo) && cincoButton.getText().equals(simbolo) && seisButton.getText().equals(simbolo)) {
        siHayGanador = true;
    }

    if (tresButton.getText().equals(simbolo) && seisButton.getText().equals(simbolo) && nueveButton.getText().equals(simbolo)) {
        siHayGanador = true;
    }
    if (sieteButton.getText().equals(simbolo) && ochoButton.getText().equals(simbolo) && nueveButton.getText().equals(simbolo)) {
        siHayGanador = true;
    }

    if (tresButton.getText().equals(simbolo) && cincoButton.getText().equals(simbolo) && sieteButton.getText().equals(simbolo)) {
        siHayGanador = true;
    }

    if (dosButton.getText().equals(simbolo) && cincoButton.getText().equals(simbolo) && ochoButton.getText().equals(simbolo)) {
        siHayGanador = true;
    }
    return siHayGanador;
}

Verificar si ganó

Minuto 13:30

Método verificarSiGano

public void verificarSiGano(String simbolo) {
    if (gano(simbolo)) {
        Toast.makeText(getApplicationContext(), "Gano " + simbolo + "!!!", Toast.LENGTH_SHORT).show();

        String nombreJugador1 = jugador1EdiText.getText().toString();
        String nombreJugador2 = jugador2EdiText.getText().toString();

        int quienGano = 0;
        if (simbolo == "X") {
            quienGano = 1;
        }
        if (simbolo == "O") {
            quienGano = 2;
        }

        Date fechaActual = Calendar.getInstance().getTime();

        Partida partida = new Partida(nombreJugador1, nombreJugador2, quienGano, fechaActual);

        partidasAdapter.add(partida);

        limpiar();
    }
}

Adapter

Minuto 14:52

Clase Partida

public class Partida {

    private String nombreJugador1;
    private String nombreJugador2;
    private int quienGano;
    private Date fecha;

    // lo demás se genera con Android Studio
}

Adapter

public class PartidasAdapter extends RecyclerView.Adapter<PartidasAdapter.ViewHolder> {

    private ArrayList<Partida> dataset;


    public PartidasAdapter() {
        dataset = new ArrayList<>();
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_partida, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Partida partida = dataset.get(position);

        if (partida.getQuienGano() == 1) {
            holder.tituloTextView.setText(partida.getNombreJugador1());
        } else {
            holder.tituloTextView.setText(partida.getNombreJugador2());
        }

        holder.subtituloTextView.setText(darFormato(partida.getFecha()));
    }

    @Override
    public int getItemCount() {
        return dataset.size();
    }

    public static String darFormato(Date date) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("ddMMMyyyy hh:mm");
        return simpleDateFormat.format(date).toUpperCase();
    }


    public static class ViewHolder extends RecyclerView.ViewHolder {

        TextView tituloTextView;
        TextView subtituloTextView;

        public ViewHolder(View itemView) {
            super(itemView);
            tituloTextView = (TextView) itemView.findViewById(R.id.tituloTextView);
            subtituloTextView = (TextView) itemView.findViewById(R.id.subtituloTextView);
        }
    }

    public void add(Partida partida) {
        dataset.add(partida);
        notifyDataSetChanged();
    }

    public void clear() {
        dataset.clear();
        notifyDataSetChanged();
    }

}

Diseño Item Partida

Minuto 16:10

item_partida.xml

Aún se deben adicionar los IDs.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="72dp"
    android:orientation="horizontal"
    android:paddingBottom="8dp"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="8dp">

    <ImageView
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:layout_gravity="center_vertical"
        android:src="@mipmap/ic_launcher" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_vertical"
        android:orientation="vertical"
        android:paddingLeft="32dp"
        android:paddingRight="16dp">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/app_name"
            android:textColor="@color/colorPrimaryText"
            android:textSize="16sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/app_name"
            android:textColor="@color/colorSecondaryText"
            android:textSize="14sp" />
    </LinearLayout>

</LinearLayout>

Formato para fechas

Minuto 17:39

Obtener fecha actual

Date fechaActual = Calendar.getInstance().getTime();

Dar formato a una fecha

public static String darFormato(Date date) {
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("ddMMMyyyy hh:mm");
    return simpleDateFormat.format(date).toUpperCase();
}

Limpiar

Minuto 19:52

Método limpiar

private void limpiar() {
    unoButton.setText("");
    dosButton.setText("");
    tresButton.setText("");
    cuatroButton.setText("");
    cincoButton.setText("");
    seisButton.setText("");
    sieteButton.setText("");
    ochoButton.setText("");
    nueveButton.setText("");
}

También puede usarse setText(null)

Código

Código completo de la aplicación: