Etiqueta

14 de diciembre de 2017

Android Studio vs Omnis Studio

En éste artículo quiero comparar lo que supone crear una aplicación sencilla para un dispositivo Android, con dos diferentes herramientas para el desarrollo de "App's": Android Studio (exclusiva de la plataforma y gratuita) y Omnis Studio (multi-plataforma y de pago). Queremos que la aplicación sea capaz de crear una base de datos SQLite en el dispositivo, con tres campos: ID, Nombre y Apellido y cuatro botones para iteracción con el usuario: "Buscar", "Insertar", "Actualizar" y "Borrar", su aspecto y código necesarios se muestran a continuación:

Aspecto con Android Studio

Aspecto con Omnis Studio

Para añadir los elementos de la "interface" en ambos casos sólo ha sido necesario arrastrar y soltar componentes desde sus respectivas paletas, pero a la hora de girar el dispositivo, "Android Studio" a requerido programación adicional, mientras que con "Omnis Studio" no ha sido necesario incluir programación alguna.

A continuación indicaremos el código que ha sido necesario escribir en cada caso, empezando por la creación de la BBDD SQLite en el dispositivo:

Creación de la BBDD en Android Studio

public class Estructura_BBDD {

private Estructura_BBDD() {}

        public static final String TABLE_NAME = "datosPersonales";
        public static final String NOMBRE_COLUMNA1 = "id";
        public static final String NOMBRE_COLUMNA2 = "nombre";
        public static final String NOMBRE_COLUMNA3 = "apellido";

    public static final String SQL_CREATE_ENTRIES =
            "CREATE TABLE " + Estructura_BBDD.TABLE_NAME + " (" +
                    Estructura_BBDD.NOMBRE_COLUMNA1 + " INTEGER PRIMARY KEY," +
                    Estructura_BBDD.NOMBRE_COLUMNA2 + " TEXT," +
                    Estructura_BBDD.NOMBRE_COLUMNA3 + " TEXT)";

    public static final String SQL_DELETE_ENTRIES =
            "DROP TABLE IF EXISTS " + Estructura_BBDD.TABLE_NAME;
}

Creación de la BBDD en Omnis Studio

Do $cinst.$sqlobject.$execute('CREATE TABLE Empleados (Id INTEGER UNIQUE, Nombre TEXT, Apellido TEXT)')
      Returns IDcreatetable

Un apunte antes de continuar en cuanto a la programación asociada a cada botón, en el caso de "Andriod Studio" ha sido necesario añadir código adicional para poner los botones en modo "OnClickListener", cosa totalmente innecesaria en "Omnis Studio", salvando ésta diferencia, (no trivial) indicamos el asociado a cada botón:

Botón "Buscar" en Android Studio

botonBuscar.setOnClickListener(new View.OnClickListener() {
   @Override   public void onClick(View v) {
       SQLiteDatabase db = helper.getReadableDatabase();
       String[] projection = {
               Estructura_BBDD.NOMBRE_COLUMNA2,
               Estructura_BBDD.NOMBRE_COLUMNA3       };

       String selection = Estructura_BBDD.NOMBRE_COLUMNA1 + " = ?";
       String[] selectionArgs = { textoId.getText().toString() };

       try {
           Cursor c = db.query(
                   Estructura_BBDD.TABLE_NAME,         
                   projection,                         
                   selection,                          
                   selectionArgs,                      
                   null,                               
                   null,                               
                   null                                
           );

           c.moveToFirst();
           textoNombre.setText(c.getString(0));
           textoApellido.setText(c.getString(1));

       }catch (Exception e){
           Toast.makeText(getApplicationContext(),"No se han encontrado registros.", Toast.LENGTH_LONG).show();
       }
   }
});

Botón "Buscar" en Omnis Studio

On evClick
Do lRow.$define(i_ID;i_Nombre;i_Apellido)
Do lRow.$assigncols(i_ID;i_Nombre;i_Apellido)
Do $cinst.$sqlobject.$selectfetch("Select * from Empleados where Id = @[i_ID]";lRow;kFetchAll) 
              Returns IDselectfetch

Botón "Insertar" en Android Studio

botonInsertar.setOnClickListener(new View.OnClickListener() {
    @Override    public void onClick(View v) {
        SQLiteDatabase db = helper.getWritableDatabase();

        ContentValues values = new ContentValues();
        values.put(Estructura_BBDD.NOMBRE_COLUMNA1, textoId.getText().toString());
        values.put(Estructura_BBDD.NOMBRE_COLUMNA2, textoNombre.getText().toString());
        values.put(Estructura_BBDD.NOMBRE_COLUMNA3, textoApellido.getText().toString());

        long newRowId = db.insert(Estructura_BBDD.TABLE_NAME, null, values);
        Toast.makeText(getApplicationContext(),"Insertado ID: " + newRowId, Toast.LENGTH_LONG).show();
    }
});

Botón "Insertar" en Omnis Studio

On evClick
Do lRow.$define(i_ID;i_Nombre;i_Apellido)
Do lRow.$assigncols(i_ID;i_Nombre;i_Apellido)
Do $cinst.$sqlobject.$insert("INSERT INTO Empleados (Id, Nombre, Apellido) VALUES (@[i_ID],@[i_Nombre],@[i_Apellido])";lRow)
             Returns IDinsert

Botón "Actualizar" en Android Studio

botonActualizar.setOnClickListener(new View.OnClickListener() {
    @Override    public void onClick(View v) {

        SQLiteDatabase db = helper.getWritableDatabase();

        ContentValues values = new ContentValues();
        values.put(Estructura_BBDD.NOMBRE_COLUMNA2, textoNombre.getText().toString());
        values.put(Estructura_BBDD.NOMBRE_COLUMNA3, textoApellido.getText().toString());

        String selection = Estructura_BBDD.NOMBRE_COLUMNA1 + " LIKE ?";
        String[] selectionArgs = { textoId.getText().toString() };

        int count = db.update(
                Estructura_BBDD.TABLE_NAME,
                values,
                selection,
                selectionArgs);
        Toast.makeText(getApplicationContext(),"Registro actualizado.", Toast.LENGTH_LONG).show();
    }
});

Botón "Actualizar" en Omnis Studio

On evClick
Do lRow.$define(i_ID;i_Nombre;i_Apellido)
Do lRow.$assigncols(i_ID;i_Nombre;i_Apellido)
Do $cinst.$sqlobject.$update("UPDATE Empleados SET Nombre=@[i_Nombre], Apellido=@[i_Apellido] where Id = @[i_ID]";lRow;lRow)
              Returns IDupdate

Botón "Borrar" en Android Studio

botonBorrar.setOnClickListener(new View.OnClickListener() {
    @Override    public void onClick(View v) {

        SQLiteDatabase db = helper.getWritableDatabase();
        String selection = Estructura_BBDD.NOMBRE_COLUMNA1 + " LIKE ?";
        String[] selectionArgs = { textoId.getText().toString() };
        db.delete(Estructura_BBDD.TABLE_NAME, selection, selectionArgs);
        Toast.makeText(getApplicationContext(),"Registro borrado.", Toast.LENGTH_LONG).show();
        textoId.setText("");
        textoNombre.setText("");
        textoApellido.setText("");
    }
});

Botón "Borrar" en Omnis Studio

On evClick
Do lRow.$define(i_ID;i_Nombre;i_Apellido)
Do lRow.$assigncols(i_ID;i_Nombre;i_Apellido)
Do $cinst.$sqlobject.$delete("DELETE FROM Empleados WHERE Id = @[i_ID]";lRow)
              Returns IDdelete

Notemos que en el código Android se ha evitado deliberadamente la incorporación del código necesario para tratar las excepciones, salvo en el caso del botón "Buscar" donde se usa un "try-catch", para controlar el hecho que pueda no encontrarse el registro buscado. Omnis predefine un método para tratar las excepciones denominado "$sqldone", a continuación mostramos un ejemplo de uso:

;  Se ejecuta cada vez que concluye una operación SQL (asíncrona)
;  Abandona el método con el mensaje de error, si se produce.

Do $cinst.$sqlobject.$getlasterrortext() Returns lErr
If lErr<>"OK"
Calculate i_ID as 0
Calculate i_Nombre as ''
Calculate i_Apellido as ''
Do $cinst.$showmessage(lErr;"Error")
Quit method
End If

;  Se ejecuta uno u otro proceso según el resultado
Switch pID
Case IDselecttables
If pResult.$linecount=0
Do $cinst.$creaTabla()     ;; Si no existe se crea la tabla
End If
Case IDselectfetch
Calculate lRows as pResult.$linecount
If lRows=0
Calculate i_ID as 0
Calculate i_Nombre as ''
Calculate i_Apellido as ''
Do $cinst.$showmessage("No existe el ID";"Respuesta")
Quit method
End If
Calculate i_Nombre as pResult.C2
Calculate i_Apellido as pResult.C3
Case IDcreatetable
Do $cinst.$showmessage("Tabla creada";"Respuesta")
Case IDinsert
Do $cinst.$showmessage("Inserción realizada";"Respuesta")
Case IDdelete
Calculate i_ID as 0
Calculate i_Nombre as ''
Calculate i_Apellido as ''
Do $cinst.$showmessage("Entrada borrada";"Respuesta")
Case IDupdate
Do $cinst.$showmessage("Actualización realizada";"Respuesta")
Default
Calculate i_ID as 0
Calculate i_Nombre as ''
Calculate i_Apellido as ''
Do $cinst.$showmessage("Error inesperado")
End Switch

En ocasiones los programadores y compañías dedicadas al desarrollo y mantenimiento de software, nos preguntamos ¿Porqué pagar por una herramienta, cuando existe otra que es gratis?, pero definitivamente puedo decir que en muchas ocasiones lo "barato" sale caro. Omnis Studio permite el desarrollo de aplicaciones para dispositivos móviles (Android, iOS y Windows), aplicaciones de escritorio (MacOS, Windows, Linux) o aplicaciones para la Web, sin necesidad de usar más herramientas que el propio Omnis Studio. El ejemplo de eficacia que hemos expuesto, compara una herramienta que sólo permite el desarrollo para dispositivos Android, por lo que el código resultante no podrá ser ejecutado en (por ejemplo) un dispositivo iOS, en cambio el código Omnis, puede ser ejecutado sin cambio alguno, sobre dispositivos iOS o Windows 10.

Juzguen ustedes mismos sobre si lo "barato" sale caro, llevo trabajando con Omnis Studio por unos 20 años ya, período durante el que las tecnologías han sufrido muchos y diferentes cambios que han hecho, al mismo tiempo, aparecer y desaparecer lenguajes de programación, pero Omnis Studio a sabido adaptarse a los tiempos y a tiempo con gran lujo y esmero.

Programar con Omnis Studio hace que me guste éste trabajo.

13 de noviembre de 2017

Componentes JSON&JS (Parte 12 de 12)

El JavaScript     

Una vez creado el fichero JSON del control y agregado a la carpeta Omnis correspondiente, será el momento para añadir el archivo JavaScript que le da soporte (si hemos usado el "JSON Control Editor" esto se habrá hecho automáticamente). Para hacerlo manualmente deberemos añadir lo siguiente al fichero html, que cargue nuestro formulario remoto:
 ≤script type="text/javascript" src="scripts/ctl_net_omnis_mycontrol.js"≥≤/script≥
Si lo colocamos bajo la sección para scripts del archivo "jsctempl.htm" (en la carpeta html) haremos que siempre se incluya en la página HTML que Omnis genera automáticamente para probar nuestro formularios remotos, pero, para la aplicación final, también deberemos incluirlo en la página HTML que lance la aplicación web o móvil desplegada.

6 de noviembre de 2017

Componentes JSON&JS (Parte 11 de 12)

html    

Es obligatorio y permite especificar cómo se generará el HTML inicial que remitirá al cliente el control (es el contenedor del control). Posee los siguientes elementos:
  • template
Obligatorio. Básicamente consiste en un texto que conforma la plantilla con el "div" que contendrá el control. Por ejemplo: ≤div %o %s data-props='%p' data-mvprops=‘%m’≥≤/div≥:

JsControls sustituirá el "%o" por los atributos del JavaScript, mediante su "id", por lo que deberá especificarse este elemento, reemplazará el "%s" con el atributo de estilo para la "div", basándose en las propiedades que soporta el control, también reemplazará el "%p" según las propiedades de control que siempre que no sean multi-valor. "%p" es reemplazado por una cadena JSON, que describe un objeto, donde cada uno de sus miembros contiene el nombre de la propiedad y su valor. 
El valor puede haber sido asignado por Omnis según lo requerido, para ciertos tipos de propiedades, tales como "color" e "icon". El cliente JavaScript usará esta cadena para componer un objeto adicional, con la configuración de la propiedad.

JsControls también reemplazará "%m" con las propiedades de control multi-valr. "%m" deberá omitirse si el control no hace uso de éste tipo de propiedades. Como en el caso de "%p", "%m" es sustituido por una cadena JSON, excepto que se trata de una matriz de objetos, con una entrada para cada elemento o valor-múltiple.
  • extrastyles
Opcional. Texto no superior a 255 caracteres con los atributos de estilo extra para incluir junto con el atributo de estilo "%s" en la plantilla, por ejemplo: "Margen: 2px".
  • padding
Opcional. Deberá ser un entero utilizado para establecer el "padding" (en píxeles) junto con "%s".
  • relativeposition
Opcional y booleano. (por defecto "false") Si es "true", el atributo de estilo que reemplaza "%s" usará posicionamiento relativo en lugar de absoluto. 
  • nowrap
Opcional y booleano. (por defecto "false") Si es "true", el atributo style que reemplaza el "%s" incluirá "white-space nowrap".
Ejemplo:
"html": {
"template": "
", "extrastyles":"margin:1px;"
}
La "div" resultante para el control podría verse así:

≤div style='position:absolute; top:0px; left:0px; height:106px; width:88px; font- family:'Times New Roman',Georgia,Serif; font-size:12pt;font- weight:bold; font- style:italic;text-align:right;color:#00FFFF; overflow- x:auto; overflow-y:auto;margin:1px;' data-backgroundcolor='#555555; rgba(85,85,85,1.0000)' data-dragmode='1' data- effect='1' data-linestyle='1' data-bordercolor='16711935' data- props='{"headercolor":"#FF0000","headericon":"icons/datafile/omnispic/001663 n16.png? 46", "rangeofinternalconstants":14, "rangeofexternalconstants":5, "headerpattern": 1, "headerfontstyle":4, "headerlinestyle":7, "headermultiline":"Lots of text entered like this\rwith multiple\rlines\r", "headerset": 13, "headerremotemenu":"NewRemoteMenu", "headerfont":"Courier New,Monospace"}'  data- mvprops='[{"mvprop1":1,"mvprop2":false,"mvprop3":""}, {"mvprop1": 2,"mvprop2":true, "mvprop3":"NewRemoteMenu"}, {"mvprop1": 2,"mvprop2":true,"mvprop3":"aaaa"}]'≥≤/div≥
Es importante el uso de comillas simples para encerrar los atributos de la plantilla, ya que JSON incluye comillas dobles. JsControls escapará el uso de comillas simples en el JSON que se inserten en como \u0027.


customtabname

El elemento "customtabname" es opcional y permite especificar el nombre que tendrá en la ficha de propiedades el control, al mostrarse sobre el "Property Manager".