miércoles, 14 de mayo de 2014

Corona SDK - Juego de naves (I) : Introducción, Elección de la herramientas y el lenguaje Lua.

Introducción


  Hoy en día dado el auge de las aplicaciones para dispositivos móviles, existe una gran cantidad de herramientas para los desarrolladores para facilitar el trabajo y es muy importante elegir correctamente la tecnología a usar para nuestro proyecto ya que esto nos facilitará mucho las cosas.

 Elección de la herramienta


  Una vez tengamos nuestra idea bien detallada nos podemos poner en la
búsqueda de las herramientas necesarias para desarrollar nuestra idea. Sin embargo el abanico de herramientas es, por suerte, muy amplio y podremos vernos desbordados sin saber cual elegir.

  Existen multitud de cuestiones que debemos tener en cuenta para esta elección, por ejemplo:

  • Desarrollar para una plataforma concreta o que sea multi-plataforma.
  • Que la curva de aprendizaje sea mínima o disponemos del tiempo suficiente para aprender a usarlo. 
  • Elegir entre gráficos 2D o gráficos 3D.
  • Trabajar con modelos 3D de objetos o con imágenes planas.
  • Que sea gratis o contamos con una apoyo económico y podemos permitir pagar cierta cantidad para el uso de la herramienta.
  • Que esté bien documentado o cuente con un refuerzo importante por parte de la comunidad ya que esto puede reducir considerablemente el tiempo de desarrollo. 
  • Y otras muchas cuestiones relativas a nuestro proyecto y más personales como puede ser: el acceso a los componentes físicos del dispositivo (acelerómetro, cámara, etc), conectividad a internet (con facebook, twitter, etc), manejo con las bases de datos, etc.
  No es necesario que la herramienta cumpla con todos los puntos pero si que cubra el máximo de ellos y que las ventajas superen a los inconvenientes de manera personal.

  Estas son algunas de las herramientas que podemos encontrar por el mercado:

-  Unity3D : un motor de videojuegos 2D/3D muy popular y con una potencia extraordinaria tanto para juegos 2D o 3D. Además con la ultima actualización podemos publicar nuestra aplicación para dispositivos moviles de forma gratuita.

-  Cocos-2D : es un framework muy enfocado a la creación de juegos 2D. Tiene la peculiaridad de que si queremos desarrollar juegos para una plataforma distinta a IOs debemos hacerlo en C++ y pelearnos con los punteros tipicos de este lenguaje para la gestión de escenas.

-  Corona : un framework enfocado tanto a la creación de juegos como a la de apps de de todo tipo. Esto último es posible gracias a que podemos incluir bibliotecas como OpenGL, Facebook Connect o Google Maps entre otras.

-  AddEngine : motor de juegos de codigo abierto con el cual podremos desarrollar nuestra aplicaciones en Java. Tambien posee MultiTouch o Red multijugador entre otras características. Sin embargo cuenta con una excasa documentación oficial teniendo que recurrir a los foros para dar con las soluciones a nuestros peores problemas.

  Sin embargo para este blog y en esta ocasión nos quedaremos con Corona ya que en mi opinión nos ofrece la posibilidad de elaborar un juego 2D potente y de forma muy sencilla, abstrayendonos casi por completo de los detalles de más bajo nivel.


LUA


  El lenguaje utilizado para desarrollar en Corona es Lua. El objetivo de este blog no es aprender este lenguaje sin embargo combiene dar algunas menciones especiales:

  Lua es un lenguaje no tipado, es decir en una variable podemos almacenar cualquier elemento ya sean datos o funciones. La forma de declarar una variable es la siguiente:

-  Local nombreVariable : si queremos definir la variable de forma local al módulo o función en la que estamos trabajando.
-  _G.nombreVariable : si queremos definir una variable de forma global. Tambien se puede definir una variable global sin nada delante, es decir:

-  nombreVariable

  Sin embargo una vez se interprete esta sentencia esta variable será introducida igualmente en la tabla _G produciendo el mismo resultado. Por tanto es recomendable utilizar la primera forma para distinguir cuando queremos declarar una variable global de cuando se nos ha olvidado poner "local" delante de una variable.

  En mi opinión esta forma de declarar variables globales es un tanto peligrosa ya que existen palabras reservadas para algunas variables del entorno o  bibliotecas específicas de Corona en la tabla _G y si tuviéramos la suerte de declarar nosotros alguna variable con el mismo nombre de alguna de estas variables reservadas, machacaríamos su valor y podríamos provocar errores indeseados. Por ejemplo:

  _G.debbug = "lo que sea"

 Esto provocaría machacar la biblioteca que usa Corona para los mensajes de depurarción y errores mostrados por consola.

  Mi consejo sería definirnos nosotros nuestra propia tabla de variables globales en un modulo aparte, por ejemplo globalVar.lua, y cuando quisiéramos utilizar alguna variable en otro modulo, importemos el módulo globalVar.lua mediante la sentencia require("globalVar.lua") para acceder a dicha tabla.

  En Lua existe un tipo especial llamado "table". Las tablas son colecciones de objetos con la característica de que podemos indexar la tabla de la forma tradicional mediante indices o mediante entradas de tabla:

-  local t = {4, 5} -- nos crea una tabla de dos elementos
   print(t[1]) -- imprime 4

-  local t = {}
   t.usuario = "Juan" -- esto crearía una entrada con el nombre "usuario"  
   print(t.usuario) -- imprime Juan

  Como podemos observar una característica importante de esta colección es que el índice del primer elemento es "1".

  Para más información sobre este lenguaje podemos consultar el siguiente enlace para la documentación oficial sobre Lua 5.1:

http://www.lua.org/manual/5.1/es/manual.html

Animación SwipeList en Android

Introducción

  En el siguiente post expondremos como hemos añadido a nuestra aplicación varias animaciones para cuando el usuario realiza el gesto "swipe" sobre un elemento de una lista para que se visualice la acción asociada a ese gesto mientras se realiza el mismo.

  Una de los principales puntos de un diseño centrado en el usuario es que la interfaz sea agradable y fluida. Por ejemplo si desarrollamos una aplicación para la lectura de documentos el cambio de página no se limite simplemente a dejar de mostrar la pagina actual y mostrar la siguiente, sino que cuando el usuario deslice el dedo por la pantalla se realice una animación de transición del cambio de página entre la nueva y la actual y produzca el efecto visual de leer un libro real.

  Android nos proporciona diferentes animaciones para esta transiciones intermedias y en nuestro caso la que nos interesaba era la animación "Screen Slider" de tal forma que cuando el usuario deslice el dedo sobre un elemento de una lista se produzcan acciones diferentes para ese elemento y además la vista del elemento cambie conforme se mueve el dedo para mostrar la acción que se va a realizar. En nuestro caso las acciones serían las siguientes:

-  Si se desliza de izquierda a derecha el elemento se actualiza.
-  Si se desliza de derecha a izquierda el elemento queda seleccionado para después poder borrarse.

  Sin embargo esta animación que nos proporciona Android tiene una pega y es que por defecto una vez que se detecta el gesto, la animación se reproduce de principio a fin y no tenemos la posibilidad de ajustarla al movimiento del dedo de tal forma que si el usuario inicia el gesto y de repente decide cancelar la acción no habría posibilidad de parar y volver atrás en la animación.

  Una posible solución a este problema sería el de definir la animación paso a paso para tener un mayor control sobre ella. Sin embargo este trabajo nos lo podemos ahorrar gracias a la amplia comunidad de Android y concretamente al grupo "47deg". Este grupo no da la solución y nos proporciona una biblioteca para realizar dos tipos de animaciones para cuando el usuario realiza el gesto "Swipe" sobre un elemento de una lista. Una aplicación de ejemplo para comprender mejor esto la puedes encontrar en su cuenta "gitHub" en la siguiente dirección:

- https://github.com/47deg/android-swipelistview-sample

Instalación

  En nuestro caso no pudimos utilizar la herramienta "gradle" para generar el jar de la biblioteca así que para poder integrar la biblioteca a nuestro proyecto lo que hemos hecho a sido integrar el código de la biblioteca en nuestro proyecto: concretamente introduciendo el paquete "fortysevendeg" en nuestro proyecto.

  Para resolver las dependencias necesarias para esta biblioteca nos bajamos la ultima versión de la biblioteca "nineoldandroids" y la integramos en nuestro proyecto. La podéis encontrar en la siguiente cuenta de gitHub:

- https://github.com/JakeWharton/NineOldAndroids/

SWIPE LIST VIEW Library

  Antes de pasar a utilizar la biblioteca explicaremos brevemente el contenido de la misma:
  
  -  SwipeListView: esta clase define un listView con la posibilidad de visualizar la animación al realizar el gesto swipe sobre un elemento de la lista. La clase define cuatro modos para activar o desactivar las diferentes animaciones:
 
   -  SWIPE_MODE_RIGHT: activar solo la animación de transición de izquierda a derecha.

   -  SWIPE_MODE_LEFT: activar solo la animación de transición de derecha a izquierda.
   -  SWIPE_MODE_BOTH: activar ambas animaciones
   -  SWIPE_MODE_NONE: no se activa ninguna.

 El atributo "swipeMode" lo podemos cambiar mediante la siguiente función pasándole alguno de los diferentes modos:
 
 setSwipeMode(int modoNuevo)

 -  BaseSwipeListViewListener: esta clase "abstracta" implementa la interfaz SwipeListViewListener la cual define las diferentes acciones que podemos realizar cuando se produce un cambio en un elemento de la lista: por ejemplo podemos realizar un acción concreta cuando la animación finaliza. La ventaja de utilizar esta clase y no implementar directamente la interfaz es que podemos sobreescribir solo los métodos que queramos usar. 

 Utilización de la biblioteca

  Ahora veremos como utilizar esta biblioteca y como dar solución a nuestro problema de la forma más fácil:

  Lo primero que debemos hacer es crearnos el layout para nuestro elemento de la lista el cual deberá estar formado por dos capas. En nuestro caso son las siguientes:

- La capa delantera compuesta de varias etiquetas de organizadas en un linearLayout.
- Y la capa trasera compuesta por una sola etiqueta correspondiente a la acción de "editar".

  La estructura del layout sería la siguiente (se he omitido los atributos no relevantes para este caso):

<FrameLayout
...
>
   <LinearLayout
   android:id = "@+id/back"
   android:tag="back"
   ...
   >
      <TextView ... >
   </LinearLayout>

   <LinearLayout
   android:id = "@+id/front"
   android:tag = "front"
   ...
   >
     <TextView ... >
   </LinearLayout>
</FrameLayout>
 


Y definir el layout para nuestra lista de elementos del tipo SwipeListView:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <com.fortysevendeg.swipelistview.SwipeListView
        xmlns:swipe="http://schemas.android.com/apk/res-auto"
        android:id="@id/android:list"
        android:listSelector="#00000000"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        swipe:swipeFrontView="@+id/front"
        swipe:swipeBackView="@+id/back"
        swipe:swipeCloseAllItemsWhenMoveList="true"
        swipe:swipeMode="both"/>
        <!-- swipe:swipeDrawableChecked="@drawable/choice_selected" swipe:swipeDrawableUnchecked="@drawable/choice_unselected" -->

</LinearLayout>
 

Pues bien ya solo nos queda utilizar la biblioteca para crearnos nuestro listView animado. 

public class ExamListFragment extends ListFragment {
 
@Override
    public void onActivityCreated(Bundle savedInstanceState){
        super.onActivityCreated(savedInstanceState);
        ExamListListener examListener;
        SwipeListView swipeListView = (SwipeListView) getListView();
       
        swipeListView.setSwipeActionLeft(SwipeListView.SWIPE_ACTION_CHOICE);
        swipeListView.setSwipeOpenOnLongPress(false);
       
        examListener = new ExamListListener(this, swipeListView, actionListener);
       
        swipeListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
            swipeListView.setMultiChoiceModeListener(examListener);
       
        swipeListView.setSwipeListViewListener(examListener);
        swipeListView.setOnItemClickListener(examListener);
    }
 


public static class ExamListListener extends BaseSwipeListViewListener implements AbsListView.MultiChoiceModeListener{

 @Override
        public void onOpened(int position, boolean toRight) {
            // Aqui se llama cuando la animación de abrir termina
        }

        @Override
        public void onItemCheckedStateChanged(ActionMode mode, int position,
                long id, boolean checked) {
            mode.setTitle("Selecionado/s (" + swipeListView.getCountSelected() + ")");
        }

        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            switch (item.getItemId()) {
            case R.id.action_delete:
                

                // ...

                return true
            default:
                return false;
            }
        }

        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.main_context, menu);
            return true;
        }

        @Override
        public void onDestroyActionMode(ActionMode mode) {
            swipeListView.unselectedChoiceStates();
        }

        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return false;
        }


}



 Lo primero que hacemos es obtener el ListView de nuestra lista que será una instancia de la clase SwipeListView. Una vez obtenida la instancia establecemos la acción de dejar seleccionado el elemento cuando se produzca un "swipe" de derecha a izquierda y quitamos la acción correspondiente al "long click" ya que por defecto este gesto tiene asignada la acción de abrir el elemento. Después creamos una instancia del objeto ExamListListener el cual será el encargado de recibir todos los eventos producidos sobre los elementos de la lista y realizar las acciones oportunas.

domingo, 11 de mayo de 2014

Loaders de Android (II): Implementar nuestro propio Loader


Implementar Loaders

Como el origen de nuestro interés por implementar nuestro propio Loader es la negativa a usar la clase CursorLoader, veamos que nos aporta esta clase para saber que deberíamos incluir en la nuestra:
  • Carga asíncrona de datos. La mayoría lo conseguimos heredando de AsyncLoader pero aún así tendremos que añadir código para manejar los datos dentro de la clase durante las distintas etapas. Afortunadamente este código es totalmente reutilizable, así que nos puede servir para distintos Loaders.
  • Consulta encapsulada. Al usar el modelo de consulta de los ContentProvider, CursorLoader proporciona una manera de expresar las consultas usando diversos parámetros (uri, projection...). Como queremos cumplir con el primer apartado, tendremos que ser capaces de expresar las consultas sin llegar a realizarlas.
  • Monitorización de datos. Cuando los datos originales cambian CursorLoader es notificado. Podemos obtener esta funcionalidad fácilmente, aunque nos obligará a añadir cierta funcionalidad a la fuente de datos.

Estos serían los tres "pilares" de nuestro Loader. No solo podemos conseguir implementarlos todos sino que podemos tener una solución aún mas independiente de la fuente de datos y del formato de los datos y con una monitorización con mayor alcance.


Carga asíncrona

Para este apartado se sigue como fuente principal el artículo de Alex Lockwood sobre implementar Loaders. No solo recomiendo leer el artículo completo (son cuatro partes), sino que también recomiendo echarle un vistazo a su blog sobre Android porque hay artículos bastante interesantes.

Como se ha comentado antes, gran parte de esta funcionalidad se consigue heredando de la clase abstracta AsyncLoader. La implementación de nuestro Loader pasa por implementar correctamente algunas de las funciones definidas en AsyncLoader:
  • loadInBackground(): Es la función donde realizaremos la lectura de datos en sí. En este primer apartado no tendremos la consulta completa ya que profundizaremos en eso en el segundo apartado.
  • deliverResult(): Función llamada para enviar el resultado de la lectura al LoaderManager.
  • onStartLoading(): Función llamada cuando LoaderManager inicia nuestro Loader.
  • onStopLoading(): Función llamada cuando LoaderManager detiene nuestro Loader.
  • onReset(): Función llamada cuando LoaderManager desactiva nuestro Loader.
  • onCanceled(): Función llamada cuando LoaderManager cancela la lectura de datos.
  • releaseResources(): Esta realmente no es un método de AsyncLoader sino del código de Lockwood. Sirve para hacer una limpieza de los resultados de la lectura cuando estos pasan a ser inválidos. Por ejemplo, en el caso de usar Cursor (allá vosotros), tendríamos que incluir aquí una llamada a close().

En el artículo de Lockwood se nos presenta una plantilla para implementar nuestros Loaders. Usaremos este código para implementar nuestro primer pilar, pero lo iremos modificando para conseguir un loader más genérico. A continuación vemos una versión "traducida" del código:

public class SampleLoader extends AsyncTaskLoader<List<SampleItem>> {

    // Mantenemos una referencia al resultado obtenido
    private List<SampleItem> mData;

    public SampleLoader(Context ctx) {
        super(ctx);
    }

    @Override
    public List<SampleItem> loadInBackground() {
        List<SampleItem> data = new ArrayList<SampleItem>();
        // TODO: Realizar la verdadera consulta (apartado 2)
        return data;
    }

    @Override
    public void deliverResult(List<SampleItem> data) {
        if (isReset()) {
            // Si el Loader ha sido desactivado liberamos los resultados y salimos.
            releaseResources(data);
            return;
        }

        // Mantenemos una referencia a los resultados anteriores para que
        // el recolector de basura no los elimine hasta que hallamos entregado los
        // nuevos resultados.
        List<SampleItem> oldData = mData;
        mData = data;
    
        if (isStarted()) {
            // Si el Loader está en estado iniciado entregamos los resultados.
            // La clase AsyncLoader se encargará de ello.
            super.deliverResult(data);
        }

        // Liberamos los viejos resultados ahora que hemos entregado los nuevos.
        if (oldData != null && oldData != data) {
            releaseResources(oldData);
        }
    }

    @Override
    protected void onStartLoading() {
        if (mData != null) {
            // Si ya tenemos resultados los entregamos.
            deliverResult(mData);
        }

        //TODO: Registrarse a la fuente de datos para monitorizar cambios (apartado 3)

        if (takeContentChanged() || mData == null) {
            // Si hemos sido notificados de cambios o si no tenemos resultados
            // iniciamos la lectura de datos.
            forceLoad();
        }
    }

    @Override
    protected void onStopLoading() {
        // Intentamos cancelar la lectura de datos.
        cancelLoad();
    }

    @Override
    protected void onReset() {
        // Nos aseguramos de que el Loader ha sido detenido
        onStopLoading();

        // Liberamos los resultados que manteniamos
        if (mData != null) {
            releaseResources(mData);
            mData = null;
        }

        // TODO: Eliminar el registro de la fuente de datos (apartado 3)
    }

    @Override
    public void onCanceled(List<SampleItem> data) {
        // intentamos cancelar la lectura de datos.
        super.onCanceled(data);

        // Liberamos los resultados
        releaseResources(data);
    }

    protected void releaseResources(List<SampleItem> data) {
        // En este ejemplo no es necesario añadir código aqui.
    }
}


Con esto ya tenemos un Loader que realizará la lectura de datos en segundo plano. Sin embargo, el hecho de que la lectura en sí forme parte de su implementación hace necesario que tengamos una clase Loader por cada lectura distinta. Este problema se soluciona en el siguiente apartado.


Consulta encapsulada

Lo bueno del código de Lockwood es que, aparte del método loadInBackground(), todo es reutilizable. Lo único que varía es la consulta de datos en sí. Por ello debemos extraer la consulta y así podremos reutilizar todo lo demás. Además podemos conseguir un añadido sobre CursorLoader: tener un Loader independiente del tipo de dato que devuelva la consulta. Hay dos maneras de conseguir esto: mediante herencia y mediante composición.

Mediante herencia: Convertimos la implementación antes vista en una clase abstract AbsLoader<D> (ahora debemos parametrizarla para manejar cualquier tipo de dato). Mantenemos la implementación de todos los métodos excepto de loadInBackground(), que dejaremos sin implementar. Para tener lecturas concretas solo tenemos que crear una clase que herede de AbsLoader y que proporcione la lectura de datos implementando loadInBackground().

public abstract class AbsLoader<D> extends AsyncTaskLoader<D> {
    // ...
}


Mediante composición: Delegaremos la lectura de datos en un objeto externo. Para ello creamos la interfaz Query<D> con un solo método, execute(), que nos devolverá un objeto de tipo D. Solo queda que modifiquemos SampleLoader para que utilice este objeto en loadInBackground():

public class BaseLoader<D> extends AsyncTaskLoader<D> {

    // ...
    private Query<D> query;

    public SampleLoader(Context ctx, Query<D> query) {
        this.query = query;
        super(ctx);
    }

    @Override
    public D loadInBackground() {
        return query.execute();
    }

    // ...
}


Sea cual sea el método escogido ya tenemos un Loader básico que podremos reutilizar con distintos tipos y fuentes de datos.


Monitorización de datos

Si queremos podemos hacer que nuestro Loader sea notificado cuando los datos que ha leído cambian en la fuente original. Lo más sencillo es aplicar un patrón observador. No es la intención de este post el explicar este patrón de diseño. Para ello hay mejores fuentes (como esta o esta otra).

Definimos una interfaz Observer (con un solo método update()) que nuestro Loader implementará de la siguiente manera:

@Override
public void update(){
    onContentChanged();
}

Definimos la interfaz Subject que será la encargada de administrar los Observer y notificarles. Podemos hacer que la propia fuente de datos implemente la interfaz o creamos explícitamente una clase que la implemente y en la fuente de datos guardamos una instancia de esta clase. En cualquier caso debemos añadir una llamada a notifyObservers() al final de cada método que realice cambios en los datos.

Por último solo queda modificar nuestra clase Loader para que acepte objetos Subject y se registre o se elimine del registro cuando sea necesario (en el lugar donde se colocaron comentarios TODO en el primer apartado):

public class BaseLoader<D> extends AsyncTaskLoader<D> implements Observer {
    private D mData;
    private Query<D> mQuery;
    private ArrayList<Subject> mSubjects;
    private boolean registered;

    public ObserverLoader(Context context, Query<D> query){
        super(context);
        registered = false;
        mQuery = query;
        mSubjects = new ArrayList<Subject>();
    }

    public ObserverLoader(Context context, Query<D> query, Subject dataSubject) {
        super(context);
        registered = false;
        mQuery = query;
        mSubjects = new ArrayList<Subject>();
        if(dataSubject != null)
            mSubjects.add(dataSubject);
    }

    public ObserverLoader(Context context, Query<D> query, List<Subject> dataSubjects){
        super(context);
        registered = false;
        mQuery = query;
        mSubjects = new ArrayList<Subject>(dataSubjects);
    }

    // ...
    @Override
    protected void onStartLoading(){
        // ...
        if(!registered){
            registered = true;
            for(Subject sub : mSubjects)
                sub.registerObserver(this);
        }
        // ...
    }

    // ...

    @Override
    protected void onReset() {
        // ...

        if(registered){
            registered = false;
            for(Subject sub : mSubjects)
                sub.unregisterObserver(this);
        }
    }

    // ...

    @Override
    public void update(){
        onContentChanged();
    }
    // ...
}

Podemos ver que nuestro Loader puede registrarse a más de un Subject. Esto nos da la posibilidad de que el Loader sea notificado no solo sobre cambios en los datos originales, sino cuando datos que afectan a la consulta (como filtros) cambian.

Con esto ya tenemos Loader completos y reutilizables. Si queréis ver o usar una implementación ya realizada de estos Loader, tenéis la ObserverLoaderLibrary. Cualquier feedback sobre este post o sobre la biblioteca (bugs, recomendaciones, aportes...) es agradecido.