Mini curso gratuito y en línea de "MobileProcessing: Primeros pasos"

Mini Curso : Mobile Processing Primeros Pasos

Este 11 de Agosto inicia el mini curso de 8 horas sobre el desarrollo de aplicaciones para dispositivos moviles utilizando la herramienta Mobile Processing que facilita la creacion de este tipo de aplicaciones para dispositivos moviles que soporten Java. Este mini curso se realizara totalmente en linea a traves de video conferencias apoyadas por actividades y tareas en linea que permiten practicar el desarrollo de pequeños ejercicios para conocer el ambiente de desarrollo, no es necesario tener conocimientos previos en lenguajes de programacion y/o herramientas de desarrollo, simplemente tener muchas ganas de aprender, el curso es totalmente gratuito y abierto para cualquier persona interesada en conocer esta tecnologia.

Objetivo del Curso.

Este mini curso provee una introducción al desarrollo de aplicaciones para dispositivos móviles utilizando la herramienta Mobile Processing, esta enfocado para todas las personas interesadas en comenzar a explorar la creación de aplicaciones sencillas y no es necesario tener experiencia en el desarrollo de aplicaciones.

Contenidos.

  • Sesión 01 :
    Presentación del Curso,
    Dispositivos Móviles y Mobile Processing
  • Sesión 02 :
    Dibujando Formas 2D
  • Sesión 03 :
    Interacción con el Usuario
  • Sesión 04 :
    Sonidos e Imágenes
    Cierre del Curso

Horario.

Video conferencia en vivo, los martes de 10 pm, duracion 2 horas. Este horario responde a las necesidades de las personas que laboran, trabajan o estudian y solo tienen disponible las horas de la noche para asistir al curso.

Enlaces.

Espiral dorada

Como ejemplo de demostración para las primitivas de dibujo había empezado a desarrollar una pequeña aplicación que dibujaba la espiral dorada según el cálculo del radio dorado o phi que también puede calcularse por aproximación con la serie de Fibonacci.  Este programita lo había hecho hace mucho tiempo cuando cursaba la asignatura de Series en pregrado y quería recordar viejos tiempos, ahora con MobileProcessing.

Dividí el problema en dos partes: la primera era en calcular y dibujar los cuadrados basados en el valor de phi y la segunda parte, un poco mas sencilla, era la de dibujar los correspondientes arcos en cada uno de los cuadrados anteriores.

En una tarde de pruebas con las primitivas gráficas hice la primera parte, sin embargo después de luchar mucho con el manejo de punto flotante y de descubrir que con CLDC 1.1 el mundo era mas bonito, también descubrí que MP no tiene como dibujar arcos.  Así que este programita se encuentra detenido por el momento.

Enlace: GoldenSpiral 0.1.

Manejo de punto flotante con MobileProcessing

Después de estas dos semanas de mucho trabajo, mucho cansancio, muchas cosas atrasadas, una operación de periodoncia y una amigdalitis que me impidió ir a clase este fin de semana y que apenas estoy sobrellevando hoy por fin pude volver a publicar algo.

Por otro lado, estaba preparando un ejemplo diferente para el artículo de las primitivas de imágenes, sin embargo este hace uso de operaciones con números reales y hasta donde había leido MP sólo soportaba valores numéricos enteros. Para suplir esta deficiencia MP provee las funciones itofp (de entero a punto flotante) y fptoi (de punto flotante a entero). La teoría se ve muy sencilla pero en la realidad me había enrredado bastante con las operaciones de mi ejemplo.

Si se desarrollan las aplicaciones para CLDC 1.1 ya no es necesario utilizar estas funciones de punto flotante, por el contrario, es posible utilizar los tipos de datos float y double convencionales y con ellas los métodos de la clase java.lang.Math de Java. Que pequeño detalle tan útil, me hubiera ahorrado media tarde de pruebas 😉

Para hacer esto utilizando el IDE de MP, acceda a los menúes File > Preferences y bajo la hoja Mobile seleccione el valor 1.1 del campo CLDC version.

Ya actualicé mi aplicación con esta mejora, sin embargo me topé con otro problema mayor: aparentemente MP no provee aún una función para dibujar arcos (segmentos de circunferencia) y sin ella mi aplicación queda a medias.

Seguiré investigando.

Primitivas de dibujo con MobileProcessing

De las cosas de MP que tenía pendiente revisar, aunque ya se han utilizado en varios ejemplos publicados hasta ahora, son las primitivas de dibujo que nos permitirán crear gráficos basados en puntos, lineas, triángulos, cuadrados, etc.

Estas son las funciones primitivas que provee MobileProcessing.

point(x, y)
Muestra un punto en la coordenada (x, y).
line(x1, y1, x2, y2)
Traza una línea desde (x1, y1) hasta (x2, y2).
triangle(x1, y1, x2, y2, x3, y3)
Dibuja un triángulo cuyos vértices se ubican en

(x1, y1), (x2, y2) y (x3, y3).

rect(x, y, ancho, alto) Dibuja un cuadrilátero cuya coordenada orígen es (x, y)

y se extiende con el alto y el ancho especificados.

quad(x1, y1, x2, y2, x3, y3, x4, y4) Dibuja un cuadrilátero cuyos cuatro vértices corresponden

a las coordenadas (x1, y1), (x2, y2), (x3, y3) y (x4, y4).

ellipse(x, y, ancho, alto)
Dibuja una elipse cuya coordenada orígen es (x, y) y se

extiende con el alto y el ancho especificados.

A continuación se muestran ejemplos de estas funciones primitivas.

Puntos Líneas Triángulos Cuadrados
Cuadriláteros Elípses Poligonos

Los polígonos, conjunto de vertices unidos por líneas, pueden crearse de manera libre utilizando la función vertex(x, y). Los llamados a esta función para crear los vértices deben ir inscritos este dos llamados a las funciones: beginShape() y endShape() respectivamente, las cuales enmarcan el inicio y el fin de la definición del polígono. La primera de estas últimas funciones viene con diferentes opciones de configuración según el tipo de polígono libre a generarse, consulte su documentación para información adicional.

La función color() permite crear variables de tipo color. La forma mas utilizada es basada en las definiciones RGB (Rojo – Verde – Azul) de los colores:

color c = color(102, 85, 34);  // Café utilizado en el triángulo del ejemplo.

Existen otras combinaciones de parámetros para manejar una escala de grises y el nivel de opacidad (alpha).

Las siguientes funciones gráficas se encuentran relacionadas con el manejo de colores.

background(color) Modifica el color del fondo.
fill(color) Hace que la figura dibujada por la primitiva esté rellena (si aplica) del color especificado.
noFill() Hace que la figura dibujada por la primitiva NO se encuentre rellena.
stroke(color) Modifica el color del contorno de la figura dibujada al especificado.
noStroke() Hace que la figura dibujada por la primitiva NO tenga contorno.
strokeWeight(grueso) Modifica el ancho de la línea de contorno (si aplica) al especificado.

Téngase en cuenta que las funciones fill hacen referencia al relleno del dibujo mientras que las funciones stroke hacen referencia a la línea exterior, es decir, su contorno.

Después del desarrollo del programa de ejemplo para este artículo concluyo lo siguiente con respecto a las funciones anteriormente mencionadas.

  1. La acción de la función fill aplica para rect, ellipse, triangle, quad y poligonos. No aplica para line o point.
  2. La acción de la función stroke aplica para todas las primitivas, en el caso de line y point les modifica su color.
  3. La acción de la función strokeWeight aplica para triangle, quad, line y poligonos. No aplica para rect, ellipse ni point, al menos con la versión 0007 de MP.

Para desplegar mensajes de texto a través de las primitivas de dibujo se deben realizar los siguientes pasos.

PFont fuente = null; Se crea el objeto que representará la fuente a utilizarse en el texto.
fuente = loadFont(familia, estilo, tamaño); Carga la fuente a utilizarse.

La familia puede ser FACE_SYSTEM, FACE_MONOSPACE o FACE_PROPORTIONAL.

El estilo puede ser STYLE_PLAIN, STYLE_BOLD, STYLE_ITALIC o STYLE_UNDERLINED.

El tamaño puede ser SIZE_SMALL, SIZE_MEDIUM o SIZE_LARGE.

Existen varias variaciones de esta función, consulte su documentación.

textFont(fuente); Asigna la fuente especificada como fuente activa.
text(mensaje, x, y, ancho, alto); Muestra en pantalla el texto mensaje ubicado en las coordenadas x, y de orígen y con un espacio disponible máximo de ancho x alto.

La aplicación de ejemplo para este artículo da uso de las funciones aquí expuestas mediante las siguientes instrucciones.

  • Las teclas izquierda (LEFT) y derecha (RIGHT) cambian la figura actual: rect, ellipse, triangle, quad, line, point y poligonos.
  • Las coordenadas de las figuras line, point y poligonos son generadas al azar, las demás son constantes.
  • Los colores, tanto de relleno como de contorno, son siempre calculados al azar.
  • Las teclas arriba (UP) y abajo (DOWN) aumentan o disminuyen el ancho de las líneas de las figuras (según aplique), esto es visible en modo sin relleno.
  • La tecla asterisco (*) modifica el modo de la figura entre con relleno y sin relleno. Inicialmente el modo con relleno se encuentra activo.
  • La tecla número (#) refresca la grafica que se muestra actualmente en la pantalla.

Enlace: ShapesDemo.

Tiempo del sistema con MobileProcessing

Las siguientes funciones permiten a los programas escritos en MP obtener la información de la fecha y hora del dispositivo en el cual se ejecutan.

day() Obtiene el día de la fecha del sistema
month() Obtiene el mes de la fecha del sistema.
year() Obtiene el año de la fecha del sistema.
hour() Obtiene las horas de la hora del sistema.
minute() Obtiene los minutos de la hora del sistema.
second() Obtiene los segundos de la hora del sistema.

Una función adicional relacionada con la fecha y hora del sistema es millis() la cual retorna la cantidad de milisegundos que han transcurrido desde el inicio de la aplicación.

Ejecutando el siguiente código en el emulador de SUN.

println("Fecha: " + day() + "/" + month() + "/" + year());
println("Hora: " + hour() + ":" + minute() + ":" + second());
println("Han transcurrido: " + millis() + " milisegundos");

Obtuve la siguiente información.

Fecha: 7/3/2008
Hora: 4:45:15
Han transcurrido: 18 milisegundos

Teniendo en cuenta que hoy es 6/4/2008 y que son las 11:45 pm, se concluye que debo investigar un poco mas acerca de la localización que toma el emulador.

Información del ambiente en MobileProcessing

MobileProcessing dispone del algunas funciones y variables que permiten obtener información funcional del dispositivo en el cual se ejecuta la aplicación. A continuación se referencian estas funciones y variables.

reportedMemory() Cantidad de memoria en bytes disponible.
currentMemory() Cantidad de memoria libre en bytes.
height Altura en pixels de la pantalla del dispositivo.
width Ancho en pixels de la pantalla del dispositivo.
isColor() Informa si el dispositivo tiene o no soporte de pantalla a color.
numColors() Retorna el número de colores disponibles en el display a color.

Ejecutando las siguientes instrucciones en el emulador de SUN.

println("Memoria reportada               : " + reportedMemory() + " bytes");
println("Cantidad de memoria disponible  : " + currentMemory() + " bytes");
println("Alto de la pantalla             : " + height + " pixels");
println("Ancho de la pantalla            : " + width + " pixels");
println("Soporte para color              : " + ((isColor()) ? "si" : "no"));
println("Cantidad de colores disponibles : " + numColors() + " colores");

Obtuve los siguientes resultados.

Memoria reportada               : 2097152 bytes
Cantidad de memoria disponible  : 1967160 bytes
Alto de la pantalla             : 291 pixels
Ancho de la pantalla            : 240 pixels
Soporte para color              : si
Cantidad de colores disponibles : 4096 colores

Impresión por consola con MobileProcessing

La impresión por consola no es útil para las aplicaciones móviles, sin embargo si lo es para depurar los programas mientras estamos desarrollandolos. MobileProcessing provee dos funciones muy similares para este menester: print y println.

Ambas funciones imprimen a través de la consola la versión de texto del argumento suministrado, con la diferencia que la segunda función agrega siempre un salto de línea al final de lo impreso.

Esta función se encuentra preparada para recibir cualquier tipo de datos de MobileProcessing para mostrar su contenido.

boolean    b = true;
byte       y = (byte)7;
char       c = 'x';
color      l = color(123, 251, 50);
int        i = 9;
String     s = "hola";

println(b);
println(y);
println(c);
println(l);
println(i);
println(s);

Imprime a través de la salida estándar del ambiente de desarrollo los siguientes valores.

true
7
x
-8651982
9
hola

La documentación sugiere que esta función inclusive puede manejar la impresión de arreglos unidimensionales.

boolean[] ba = {true, false, true};
byte[]    ya = {(byte)1, (byte)2, (byte)3};
char[]    ca = {'a', 'b', 'c'};
color[]   la = {#121212, #aa2211, #21ab12};
int[]     ia = {1, 2, 3};
String[]  sa = {"Hola", "Mundo", "Loco"};

println(ba);
println(ya);
println(ca);
println(la);
println(ia);
println(sa);

Sin embargo cuando experimenté con el código no obtuve los resultados esperados utilizando la versión 0006 ALFA de MP.

[Z@f828ed68
[B@ea0ef881
[C@84aee8b
[I@c5c7331
[I@e938beb1
[Ljava.lang.String;@11eaa96

Condicionales con MobileProcessing

Las sentencias condicionales moderan el flujo de ejecución de un programa según la ocurrencia de ciertas condiciones específicas, es decir, le permiten al desarrollador determinar que porciones de código se ejecutan según las circunstancias.

La sentencia condicional mas simple es el if (si de suposición), permite realizar este tipo de decisión mencionado anteriormente. Su sintáxis es la siguiente.

if (expresión)
{
// bloque de código.
}

// Siguiente instrucción.

Si la expresión al ser evaluada retorna un valor afirmativo (true) el bloque de código es ejecutado, de lo contrario (retornó false), el control del programa continúa su ejecución en la siguiente instrucción. Por ejemplo.

if (divisor != 0)
{
cociente = dividendo / divisor;
}

El código anterior realiza la división entre dividendo y divisor almacenando su resultado en la variable cociente, realizando previamente la verificación que divisor no sea cero ya que la división por cero no se encuentra definida. En el caso de que el divisor sea cero, no se ejecutará la operación.

El condicional if permite también especificar que debe suceder en el caso en que la condición del condicional no sea aceptada, es decir, determinar que código se debe ejecutar cuando la expresión del condicional al ser evaluada retornó false mediante la instrucción else (de lo contrario).

saludo = "Buenos dias";

if(hora < 12)
{
tipo = "AM";
}
else
{
tipo = "PM";
saludo = "Buenas tardes";
}

Según el ejemplo anterior se le asignará el valor de "AM" a la variable tipo si el valor almacenado en la variable hora es menor de 12 (condición), de lo contrario se le asignará el valor de "PM" y a la variable saludo se le asignará el valor de "Buenas tardes".

Para casos como el anterior, en los que el código a ejecutarse en los bloques de if o del else cuentan con una única línea de código pueden obviarse las llaves de agrupación del bloque ({ ... }). Téngase en cuenta este detalle toda vez que bien puede llevar a confusiones. Según esto, la siguiente representación del código anterior es equivalente.

if(hora < 12)
tipo = "AM";
else
{
tipo = "PM";
saludo = "Buenas tardes";
}

Nótese como no aplica para el bloque else ya que este contiene mas de una línea de código.

Lo más común es que las expresiones evaluadas en los condicionales estén basadas en la comparación de valores, tal y como se han hecho los ejemplos hasta ahora:

  • Si el divisor es diferente de cero: divisor != 0.
  • Si la hora es menor que 12: hora < 12.

Para esto son utilizados los operadores relacionales los cuales son descritos a continuación.

Igual a A == B Es verdadero si A es igual a B, es falso de lo contrario.
Diferente de A != B Es verdadero si A es diferente de B, es falso de lo contrario.
Menor que A < B Es verdadero si A es menor que B, es falso de lo contrario.
Menor o igual que A <= B Es verdadero si A es menor o es igual que B, es falso de lo contrario.
Mayor que A > B Es verdadero si A es mayor que B, es falso de lo contrario.
Mayor o igual que A >= B Es verdadero si A es mayor o es igual que B, es falso de lo contrario.

Las expresiones evaluadas por la estructura condicional se pueden componer utilizando los operadores básicos del álgebra booleana: Y (and), O (or) y NO (not).

Los operadores AND y OR son binarios, es decir, requieren de dos parámetros, mientras que el operador NOT requiere de un único parámetro. Estos operadores permiten componer la expresión booleana resultante a patir de articular múltiples expresiones con ellos, calculándose el resultado a partir de sus tablas de verdad.

La conjunción o AND indica que las dos expresiones deben ser verdaderas para que la expresión resultante así lo sea, de lo contrario, es decir, si una de las dos falla, el resultado será falso. Se representa con el operador &&.

if(expresión1 && expresión2)
// Código #1
else
// Código #2

El código #1 sólo se ejecutará en el caso en que expresión1 y expresión2, ambas, sean verdaderas. En caso de que falle (sea falsa) una de las dos, se ejecutará el código #2.

La disyunción u OR indica que al menos una de las expresiones, o las dos, deben ser verdaderas para que la expresión resultante así lo sea, en caso contrario, es decir las dos sean falsas, el resultado será falso. Se representa con el operador ||.

if(expresión1 || expresión2)
// Código #1
else
// Código #2

El código #1 se ejecutará bien sea porque expresión1 es verdadera, porque expresión2 es verdadera o porque ambas, expresión1 y expresión2 son verdaderas. En caso contrario se ejecutará el código #2. Nótese como la conjunción es mas restrictiva que la disyunción, para la primera ambas expresiones deben evaluarse de manera positiva mientras que para la segunda sólo es importante que se cumpla al menos una de ellas.

La negación o NOT es una booleana cuya finalidad consiste en negar (convertir en su complemento) al valor proporcionado. Es decir, la negación de true es false y la negación de false es true. Se representa con el operador !.

if(! expresion)
// Código #1
else
// Código #2

El código #1 se ejecutará si y sólo si la expresión es falsa, de lo contrario se ejecutará el código #2.

Como se mencionó anteriormente, es posible componer expresiones booleanas complejas a partir de la unión de múltiples expresiones mas simples con estos operadores.

if(producto_listo &&
!producto_descompuesto &&
(producto_en_almacen || producto_en_tienda))
{
// Vender el producto a los clientes.
}
else
// Solicitar la producción del producto.

En este ejemplo se decide si el producto puede ser vendido directamente a los clientes o si por el contrario deberá ser solicitada su producción. Para que sea vendido se deberán satisfacer tres condiciones sin las cuales no podrá ser vendido.

  1. Que el producto se encuentre listo.
  2. Que no se encuentre descompuesto.
  3. Que haya existencia ya sea en almacén o en la tienda.

Como las condiciones 1, 2 y 3 son de obligatorio cumplimiento están unidas ente sí por conjunciones, el requerimiento #3 da la opción de que el producto se encuentre ya sea en el almacén o en la tienda, por ello se encuentra unido con una disyunción. Si falla una de ellas se deberá realizar la solicitud del producto.

Existe un caso particular en el uso de las sentencias condicionales if que pueden ser resumidas mediante el uso del operador if ternario el cual reproduce la misma funcionalidad del condicional en una sola línea de código. Considérese el siguiente caso.

String calificacion = "";

if (nota >= 3)
calificacion = "Aprobado";
else
calificacion = "Reprobado";

println ("El examen ha sido " + calificacion);

El condicional basado en el resultado de la variable nota determina el valor de la variable calificación según la comparación con un valor conocido. Este condicional puede reescribirse de la siguiente manera utilizando la versión ternaria.

String calificacion = (nota >= 3) ? "Aprobado" : "Reprobado";

Nótese que la expresión anterior cuenta con los mismos elementos que la versión completa pero dispuestos con la siguiente estructura.

valor = (expresión) ? valor_en_caso_afirmativo : valor_en_caso_contrario;

Esta versión simplificada de la sentencia condicional es útil para este caso cuando la función de la condición es la de filtrar una expresión para determinar el valor que se involucrará en una asignación.

Existe una variación interesante para los operadores booleanos de conjunción (&&) y disyunción (||) y son sus versiones sin optimización & y | respectivamente.

Se dice que los operadores && y || se encuentran optimizados ya que ellos se evalúan parcialmente si pueden inferir su resultado final con la información parcial.

  • La conjunción requiere que las dos expresiones que la componen sean verdaderas por este motivo si la primera expresión es falsa ya no hay motivo para evaluar la segunda.
  • La disyunción requiere que al menos una de las expresiones sea verdadera, si la primera es verdadera, ya no hay necesidad de evaluar la segunda.

Esta optimización que la mayoría de las veces permite ahorrar fracciones de milisegundos en los cálculos puede ser indeseada en ciertos casos específicos. Considérese el siguiente ejemplo.

int a = 0;
int b = 0;

boolean A()
{
a ++;
return false;
}

boolean B()
{
b ++;
return true;
}

void setup()
{
if(A() && B())
println("Sorpresa!");

println("a = " + a + "; b = " + b);
}

Las funciones A() y B() retornan los valores false y true respectivamente, por este motivo no esperamos que se muestre el mensaje Sorpresa! ya que la primera expresión de la conjución es false y por ello la ejecución del programa nunca ingresará a ese bloque de código, sin embargo la impresión final si tiene una variación debido a la optimización.

Se esperaría que al final de la ejecución los valores de a y b fueran 1, sin embargo por lo explicado anteriormente serán "a = 1; b = 0".

Igual sucede para la disyunción. Considerese la siguiente modificación al ejemplo anterior.

// ...
if(B() || A())
// ...

En este caso los valores resultantes serán "a = 0; b = 1", es decir, nunca se evaluó la expresión A().

Para evitar esta situación cuando sea necesario se deben utilizar los operadores análogos sin optimización. La siguiente modificación ejemplifica su uso bajo el cual se obtienen los valores inicialmente esperados.

// ...
if(A() & B())
// ...

El resultado de la impresión final es: "a = 1; b = 1". De igual manera sucederá para la disyunción con el operador |.

Otra facilidad para la realización de comparaciones según su finalidad es la siguiente. Considérese el siguiente caso.

if(mensaje == 4 )
moverJugadorIzquierda();
else
if(mensaje == 6 )
moverJugadorDerecha();
else
if(mensaje == 2 )
moverJugadorArriba();
else
if(mensaje == 8 )
moverJugadorAbajo();
else
println("Mensaje desconocido");

El código anterior basado en una serie de sentencias if-else anidadas garantiza que sólo uno de los bloques de código se va a ejecutar, para este caso, cuando mensaje sea cualquiera de los valores 2, 4, 6, 8 o sea desconocido (ninguno de los anteriores).

Este código puede reescribirse de una manera simplificada utilizando la sentencia switch de la siguiente manera.

switch(mensaje)
{
case 4:  moverJugadorIzquierda();
break;

case 6:  moverJugadorDerecha();
break;

case 2:  moverJugadorArriba();
break;

case 8:  moverJugadorAbajo();
break;

default: println("Mensaje desconocido");
break;
}

La sentencia switch tiene las características siguientes.

  • Puede ejecutarse sobre un número o un carácter el cual es realmente un valor ASCII entero. Para el caso de los carácteres debe recordarse que estos se encierran entre comillas simples: 'a', 'B', '7', etc.
  • Por cada opción de valor (case) de la variable filtrada se pueden ejecutar cuantas líneas de código como se requiera.
  • Cada bloque de código relacionado con cada opción debe terminarse con una sentencia de corte (break), de lo contrario se ejecutará el código de las distintas opciones secuencialmente hasta que se encuentre con un break o se termine la sentencia.
  • La opción default es opcional, es decir, no es obligatoria su definición y se ejecutará únicamente si ninguna de las opciones disponibles satisfacieron el valor de la variable filtrada.

Es posible proporcionar varios valores para una misma opción, es decir, indicar que se ejecute un bloque de código si la variable filtrada tiene alguno de los valores especificados, para esto se debe seguir la sintaxis siguiente.

// ...
case 1:  case 4:
moverJugadorIzquierda();
break;
// ...

Según la modificación realizada al ejemplo anterior, la función moverJugadorIzquierda() se llamará cuando el valor de la variable mensaje sea 1 o sea 4.

Arreglos con MobileProcessing

En Tipos de datos de MobileProcessing se introdujeron los arreglos (Array) como un tipo de datos compuesto, ahora voy a ampliar brevemente su concepto.

Un arreglo, al igual que en muchos lenguajes de programación, es un objeto que encapsula una serie finita de variables del mismo tipo referenciadas a través de un índice numérico. De esta definición podemos concluir fácilmente las siguientes afirmaciones relacionadas con los arreglos.

  1. Un arreglo agrupa varias variables/objetos.
  2. Todas y cada una de las celdas de un arreglo son de un mismo tipo de datos.
  3. Los arreglos son finitos: limitados en su cantidad de celdas, teniendo en cuenta MP provee funciones para manipular su longitud en tiempo de ejecución.
  4. Las celdas se referencian a través de un índice numérico (entero) utilizando el operador de corchetes ([ ]).

Al igual que los demás objetos, la creación de un arreglo consta de dos partes que se pueden agrupar en una sola línea de código.

// Declaración del arreglo de celdas de tipo 'entero'.
// En este punto arreglo_enteros = null.
int[] arreglo_enteros;
// Creación del objeto arreglo_enteros como un arreglo
// de 25 celdas de tipo entero.  arreglo_enteros != null.
arreglo_enteros = new int[25];

Como se mencionó anteriormente este proceso se puede realizar en una sola línea de código.

int[] arreglo_enteros = new int[25];    // Instrucción equivalente.

Es posible crear arreglos de cualquiera de los tipos de datos que soporta el lenguaje, inclusive de objetos.

String[] arreglo_cadenas = new String[50];

Las celdas del arreglo se pueden acceder de manera directa tanto para lectura como para escritura a través del operador corchetes.

// Modificar el valor de la celda 13.
arreglo_cadenas[13] = "Hola Mundo";
// Obtener el valor de la celda 20.
String cadena = arreglo_cadenas[20];

Como se mencionó los arreglos son objetos, por tal motivo tienen atributos y métodos provenientes de la clase Object. En este momento nos es interesante el atributo length el cual indica siempre la longitud de arreglo, es decir, el número de celdas del mismo.

int longitud1 = arreglo_enteros.length;
// longitud1 = 25.
int longitud2 = arreglo_cadenas.length;
// longitud2 = 50.

Gracias a este atributo es posible recorrer facilmente el arreglo para obtener o modificar sus valores. A continuación se realiza una iteración que recorre una a una las celdas del arreglo y les asigna un valor.

for(int indice=0; indice
{
    arreglo_cadenas[indice] = "Elemento #" + indice;
}

El ciclo anterior hace iterar a la variable indice desde 0 hasta el valor anterior a la longitud del arreglo (arreglo_cadenas.length), aumentando el valor de la variable indice en una unidad (indice++) en cada iteración.

Al final de la ejecución de este código el contenido de las celdas del arreglo arreglo_cadenas será:

["Elemento #0", "Elemento #1", "Elemento #2", ..., "Elemento #49"]

Nótese que los indices de los arreglos empiezan en cero (0) y terminan consecuentemente en la longitud - 1 del arreglo. Es decir, un arreglo de tres elementos tendrá los índices: 0, 1 y 2 respectivamente.

Existe una forma rápida de instanciar arreglos la cual es conveniente cuando se conocen sus valores iniciales.

int[] arreglo_enteros = {10, 20, 30, 40, 50};

La instrucción anterior crea en arreglo_enteros un arreglo de 5 celdas inicializadas con los valores especificados al lado derecho del igual. Téngase en cuenta que del lado izquierdo a pesar de que no se especificó un tamaño si se especificó que iba a ser un arreglo (int[]). El tamaño del arreglo es determinado finalmente por la cantidad de elementos que se especifiquen al lado derecho de la asignación.

Para conocer mas acerca de las funciones para manipular la longitud de los arreglos consulte las funciones expand() y contract() en Tipos de datos de MobileProcessing.

Las matrices binarias (de dos dimensiones) y n-arias (de n dimensiones) no son mas que construcciones compuestas de arreglos, es decir, son arreglos de arreglos.

Una matriz entera de dos dimensiones puede utilizarse para representar el tablero del juego de Burbujas, donde 0 indica que la celda se encuentra vacía y >0 que se encuentra ocupada, siendo 1 por una burbuja azul, 2 por una verde, 3 por una naranja y 4 por una roja por ejemplo.

Para crear el objeto tablero matriz de dos dimensiones de tipo entero de 15 filas por 10 columnas utilizamos la siguiente instrucción.

int[][] tablero = new int[15][10];

Nótense los elementos adicionales respecto a la creación de arreglos.

  • Se debe indicar que el objeto será matriz de dos dimensiones: int[][]. De manera sucesiva para n-dimensiones.
  • Se deben indicar los tamaños para las dos dimensiones: primero filas (horizontales) y después columnas (verticales). Ejemplo: new int[15][10]; Esto indica que el tablero tendrá 15 filas y 10 columnas. De manera sucesiva para las n-dimensiones.

Para recorrer una matriz bidimencional se utilizan dos ciclos anidados de manera análoga a como se hizo con el arreglo en una única dimensión.

for(int filas=0; filas<15; filas++)
{
    for (int columnas=0; columnas<10; columnas++)
    {
        tablero[filas][columnas] = 0;    // Inicializado como celda vacía.
    }
}

Para acceder a posiciones específicas de la matriz utilice también el operador de corchetes espeficicando la coordenada bidimensional (fila, columna) de la posición a acceder.

// Obtiene información de la burbuja ubicada en la cuarta fila, segunda columna.
int b1 = tablero[3][1]:
// Poner en la posición (6, 3) a una burbuja azul.
tablero[6][3] = 1;