Android

Android


10.IMÁGENES

Página 12 de 25

10.   IMÁGENES

10.1.   Insertar una imagen en el layout

El sistema Android soporta imágenes en formato png, jpg y gif. Para insertar una imagen en una aplicación hay que copiar el fichero con la imagen en la carpeta res/drawable. Por defecto, hay tres carpetas, drawble-hdpi, drawable-ldpi y drawable-mdpi, correspondientes a resolución alta, baja y media, respectivamente, que serán seleccionadas por la aplicación dependiendo de la resolución de pantalla del dispositivo que se utilice.  En el siguiente ejemplo, creamos una nueva aplicación y copiamos nuestra imagen portada.jpg en la carpeta res/drawable-hdpi. A continuación, modificamos el fichero main.xml, añadiendo una etiqueta ImageView de la siguiente forma:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

       android:orientation="vertical"

       android:background="#ddffdd"

       android:layout_width="fill_parent"

       android:layout_height="fill_parent"

       >

<TextView

       android:layout_width="wrap_content"

       android:layout_height="wrap_content"

       android:textColor="#000000"

       android:text="ANDROID ESENCIAL"

       android:layout_gravity="center"/>

<ImageView

       android:layout_height="wrap_content"

       android:src="@drawable/portada"

       android:layout_width="wrap_content"

       android:id="@+id/imageView1">

</ImageView>

</LinearLayout>

Con esta estructura, la imagen se añade al layout y no es necesario hacer nada más. Al ejecutar la aplicación la imagen se muestra en la pantalla como en la figura 10.1.

Figura 10.1. Imagen insertada en un layout.

10.2.   Controlando las imágenes en Java

La siguiente actividad construye un album de fotos. Copiaremos cuatro fotos en la carpeta res/drawable-hdpi. Diseñamos el layout en main.xml incluyendo la primera de las fotos. Definimos dos botones para pasar las fotos adelante y atrás.

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

     android:background="#ddddbb"

     android:orientation="vertical"

     android:layout_width="fill_parent"

     android:layout_height="fill_parent"

     >

<TextView

     android:textColor="#000000"

     android:layout_width="fill_parent"

     android:layout_height="wrap_content"

     android:text="ALBUM DE FOTOS. Pulsa los botones para verlas"

     />

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

     android:background="#999999"

     android:orientation="horizontal"

     android:layout_width="fill_parent"

     android:layout_height="wrap_content"

     >

     <Button android:text="Anterior"

     android:id="@+id/button1"

     android:layout_width="wrap_content"

     android:layout_height="wrap_content"></Button>

     <Button android:text="Siguiente"

     android:id="@+id/button2"

     android:layout_width="wrap_content"

     android:layout_height="wrap_content"></Button>

     <TextView

     android:textColor="#000000"

     android:id="@+id/textView1"

     android:layout_width="wrap_content"

     android:layout_height="wrap_content"

     android:text="Mi bisabuelo"

     />

</LinearLayout>

<ImageView android:layout_height="wrap_content"

     android:src="@drawable/bisabuelo"

     android:layout_width="wrap_content"

     android:id="@+id/imageView1"></ImageView>

</LinearLayout>

A continuación modificamos el fichero Java de la actividad. Cada fichero foto.jpg tiene una id llamada R.drawable.foto. Para modificar la foto mostrada primero se define una variable  ImageView y luego se define la imagen a mostrar mediante setImageResource(id):

foto=(ImageView) findViewById(R.id.imageView1);

foto.setImageResource(id)

Las Ids de las cuatro fotos las introducimos en un array de enteros. Al pulsar un botón se avanza o se retrocede el índice de la fotografía a mostrar. El programa queda como sigue:

Figura 10.2. Controlando las imágenes en Java.

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.ImageView;

import android.widget.TextView;

public class FotosActivity extends Activity implements

OnClickListener{

   ImageView foto;

   TextView tv;    // para escribir el texto de la foto

   int[] fotoId = {R.drawable.bisabuelo,

            R.drawable.farmacia,

            R.drawable.fernando_amaro,

            R.drawable.tres_damas};

   String[] texto={"Bisabuelo","Abuelo","Tio","Abuela"};

   int i=0;             // numero de foto

   int total;   // numero total de fotos

   /** Called when the activity is first created. */

   @Override

   public void onCreate(Bundle savedInstanceState) {

         super.onCreate(savedInstanceState);

         setContentView(R.layout.main);

         Button anterior=(Button) findViewById(R.id.button1);

         Button siguiente=(Button) findViewById(R.id.button2);

         anterior.setOnClickListener(this);

         siguiente.setOnClickListener(this);

         tv=(TextView) findViewById(R.id.textView1);

         foto=(ImageView) findViewById(R.id.imageView1);

         total = fotoId.length;

    }

   public void onClick(View v) {

       int id=v.getId();

       // boton2 adelanta el numero de foto

       if(id==R.id.button2){

          i++;

          if (i == total) i=0;

          }

       // boton1 atrasa el numero de foto

       if(id==R.id.button1){

          i--;

          if (i == -1) i=total-1;

          }

       foto.setImageResource(fotoId[i]);

       tv.setText("Mi "+texto[i]);

   }

  }

El resultado del album de fotos se ve en las figuras 10.2 y10.3.

Figura 10.3. Controlando las imágenes en Java.

10.3.   Botones con imágenes

Un ImageButton es un tipo especial de View que es un botón con una imagen superpuesta. La forma de definir un ImageButton en xml es la siguiente:

<ImageButton

      android:background="#00000000"

      android:scaleType="centerCrop"

      android:layout_weight="1"

      android:layout_width="wrap_content"

      android:layout_height="wrap_content"

      android:src="@drawable/boton1"

      android:id="@+id/imageButton1"></ImageButton>

Al hacer el fondo transparente no se verá el botón de fondo. Sólo la imagen:

android:background="#00000000"

Existen distintos tipos de escala que se pueden aplicar a la imagen como center o centerCrop. Esta última escalará la imagen a la anchura total disponible y cortará los márgenes.

android:scaleType="centerCrop"

En el siguiente ejemplo definimos cuatro botones con cuatro fotografías.

Figura 10.4. Uso de ImageButton.

<?xml version="1.0" encoding="utf-8"?>

<TableLayout

xmlns:android="http://schemas.android.com/apk/res/android"

       android:stretchColumns="*"

       android:background="#ffeecc"

       android:orientation="vertical"

       android:layout_width="fill_parent"

       android:layout_height="fill_parent"

       >

<TableRow>

<TextView

       android:layout_span="2"

       android:textSize="20sp"

       android:textColor="#000000"

       android:layout_gravity="center"

       android:layout_width="wrap_content"

       android:layout_height="wrap_content"

       android:text="Forjadores de la Física Cuántica"

       />

</TableRow>

<TableRow>

<ImageButton

       android:background="#00000000"

       android:scaleType="centerCrop"

       android:layout_weight="1"

       android:layout_width="wrap_content"

       android:layout_height="wrap_content"

       android:src="@drawable/boton1"

       android:id="@+id/imageButton1"></ImageButton>

<ImageButton

       android:background="#00000000"

       android:scaleType="centerCrop"

       android:layout_weight="1"

       android:layout_width="wrap_content"

       android:layout_height="wrap_content"

       android:src="@drawable/boton2"

       android:id="@+id/imageButton2"></ImageButton>

</TableRow>

<TableRow>

<ImageButton

       android:background="#00000000"

       android:scaleType="centerCrop"

       android:layout_weight="1"

       android:layout_width="wrap_content"

       android:layout_height="wrap_content"

       android:src="@drawable/boton3"  

       android:android:id="@+id/imageButton3"></ImageButton>

<ImageButton

       android:background="#00000000"

       android:scaleType="centerCrop"

       android:layout_weight="1"

       android:layout_width="wrap_content"

       android:layout_height="wrap_content"

       android:src="@drawable/boton4"

       android:id="@+id/imageButton4"></ImageButton>

</TableRow>

<TableRow>

<TextView

       android:id="@+id/caption"

       android:layout_span="2"

       android:textSize="20sp"

       android:textColor="#000000"

       android:layout_gravity="center"

       android:layout_width="wrap_content"

       android:layout_height="wrap_content"

       android:text="Toque una foto para más info"

       />

</TableRow>

</TableLayout>

La imagen a mostrar debe estar en una de las carpetas res/drawable y se especifica mediante drawable/boton1, etc. Esta variable podría referirse a un fichero de imagen, por ejemplo boton1.png. Pero, en este caso, se refiere a un fichero boton1.xml que hemos creado en el directorio res/drawable-hdpi conteniendo un selector:

<?xml version="1.0" encoding="utf-8"?>

<selector

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

        <item android:state_pressed="true"

              android:drawable="@drawable/bohr_sepia" />

        <item android:drawable="@drawable/bohr" />

</selector>

este selector a su vez enlaza con los ficheros bohr.jpg (la imagen mostrada por defecto) y bohr_sepia.jpg (la imagen mostrada al pulsar el botón). Análogamente hemos creado otros ficheros llamados boton2.xml, boton3.xml y boton4.xml, conteniendo referencias a otros pares de imágenes que queremos mostrar.

La siguiente actividad utiliza el fichero main.xml anterior y muestra un mensaje al pulsar cada botón.

package es.ugr.amaro.fisicoscuanticos;

import android.app.Activity;

import android.graphics.Color;

import android.os.Bundle;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.TextView;

public class FisicosCuanticosActivity extends Activity

  implements OnClickListener{

     TextView caption;

     String[] texto={" ",

     "Niels Bohr (1885-1962)." +

     "Contribuyó a la comprensión de la estructura del átomo."+

     "Premio Nobel 1922",

     "Max Plank (1858-1947). " +

     "Sentó las bases de la teoría Cuántica de la materia " +

     "e introdujo la constante de Planck." +

     "Premio Nobel 1918",

     "Pauli. Principio de exclusión. Matrices de Pauli.",

     "Schrodinger (1887-1961) Ecuación de Schrödinger, " +

     "Modelo atómico de Schrödinger y Efecto Túnel."+

     "Premio Nobel 1933"};

     @Override

     public void onCreate(Bundle savedInstanceState) {

         super.onCreate(savedInstanceState);

         setContentView(R.layout.main);

         caption=(TextView) findViewById(R.id.caption);

         View boton1=findViewById(R.id.imageButton1);

         boton1.setOnClickListener(this);

         View boton2=findViewById(R.id.imageButton2);

         boton2.setOnClickListener(this);

         View boton3=findViewById(R.id.imageButton3);

         boton3.setOnClickListener(this);

         View boton4=findViewById(R.id.imageButton4);

         boton4.setOnClickListener(this);

     }

     @Override

     public void onClick(View v) {

       int i=0;

       int id=v.getId();

       if(id == R.id.imageButton1) i=1;

       else if(id == R.id.imageButton2) i=2;

       else if(id == R.id.imageButton3) i=3;

       else if(id == R.id.imageButton4) i=4;

       caption.setTextColor(Color.BLUE);

       caption.setText(texto[i]);

     }

  }

Vease captura de pantalla en la figura 10.4. En este caso, cada vez que se pulsa una imagen, ésta cambia momentaneamente a color sepia y se muestra un texto explicativo sobre el personaje de la fotografía.

10.4.   Insertar imágenes en un canvas

Otra forma de insertar imágenes es "dibujarlas" directamente en un canvas, dentro del método onDraw() de una clase View. De esta forma, tenemos control total sobre el posicionamiento, el tamaño y se pueden realizar manipulaciones gráficas. Para dibujar una imagen primero se declara un objeto de tipo Drawable:

Drawable imagen;

y luego debe definirse la referencia al fichero con la imagen:

imagen=context.getResources().getDrawable(

                                        R.raw.fichero_imagen);

Esto se hará en el constructor de la clase View que definamos. La imagen se dibuja en el método onDraw(), pero antes es indispensable definir sus dimensiones:

imagen.setBounds(x1,y1,x2,y2);

imagen.draw(canvas);

donde (x1,y1) y (x2,y2) son los vértices opuestos del rectángulo que contiene la imagen. Si estos puntos no se definen, la imagen no se verá en pantalla, pues se le asignarán dimensiones nulas. Para obtener las dimensiones reales de la imagen se usan los métodos getIntrinsicWidth(), getIntrinsicHeight(). En el siguiente ejemplo ilustramos esto dibujando en un canvas una imagen jpg, con sus dimensiones reales (ver figura 10.5).

Figura 10.5. Insertar imágenes en un canvas.

import android.app.Activity;

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.graphics.drawable.Drawable;

import android.os.Bundle;

import android.view.View;

public class DibujarImagen extends Activity {

    @Override

    public void onCreate(Bundle savedInstanceState) {

         super.onCreate(savedInstanceState);

         Pintura miPintura = new Pintura(this);

         setContentView(miPintura);

    }

    public class Pintura extends View{

     Drawable imagen;

     public Pintura(Context context) {

        super(context);

        int indiceImagen=R.drawable.actor_small;

         imagen=context.getResources().getDrawable(

                                        indiceImagen);

     }

     @Override

     public void onDraw(Canvas canvas){

        Paint p=new Paint();

        p.setColor(Color.WHITE);

        canvas.drawPaint(p);

        int anchura= imagen.getIntrinsicWidth();

        int altura= imagen.getIntrinsicHeight();

        imagen.setBounds(0,0,anchura,altura);

        imagen.draw(canvas);    

     }

    }

 }

El rectángulo que contiene la imagen es arbitrario y la copia se ampliará o reducirá para caber en él. La relación anchura/altura de la imagen sólo se mantendrá si aplicamos la misma escala a las dos dimensiones. La misma imagen se puede dibujar múltiples veces con distintas manipulaciones gráficas. Por ejemplo, en el anterior programa podemos dibujar dos copias de la misma imagen con distintas dimensiones y posiciones, sustituyendo el método onDraw() por el siguiente:

@Override

public void onDraw(Canvas canvas){

     Paint p=new Paint();

     p.setColor(Color.WHITE);

     canvas.drawPaint(p);

     imagen.setBounds(0,0,400,200);

     imagen.draw(canvas);

     imagen.setBounds(100,200,300,700);

     imagen.draw(canvas);

}

El resultado se ve en la figura 10.6.

Figura 10.6. Imágenes distorsionadas.

10.5.   Ajustar imagen a las dimensiones de la pantalla

Las imágenes que pongamos en la carpeta res/drawable y dibujemos por el método anterior no pueden tener cualquier tamaño, sino que este debe mantenerse inferior a cierta cantidad que dependerá del dispositivo utilizado.  En general, se recomienda que la imagen no supere los 2 Megapixeles, lo que corresponde aproximadamente a una imagen de 1200 pixeles de ancho por 1600 de alto (que contiene unos 1.9 Megapixeles). Si se supera esta cantidad, la línea de código:

Drawable image=getResources().getDrawable(R.drawable.file);

puede producir un error al ejecutarse de tipo NullPointerException.  Conviene pues reducir el tamaño de las imágenes lo más posible, teniendo en cuenta además que la resolución de las pantallas de los dispositivos móviles, incluso tablets, todavía es inferior a los 2 Megapíxeles. Por ejemplo, el tablet Motorola Xoom con pantalla de 10 pulgadas, tiene una resolución de 800 x 1280 píxeles, correspondiente a 150 pixeles por pulgada.

En cualquier caso, nuestra imagen siempre podrá reescalarse para ajustarse lo más posible a la pantalla. En el siguiente ejemplo mostramos una forma de hacerlo. Modificamos la clase Pintura del ejemplo anterior para leer una imagen que es mayor que la resolución de la pantalla. La imagen se reescala dependiendo de la razón altura/anchura de la imagen, comparada con la relación anchura/altura de la pantalla. Si la primera razón es menor que la segunda, se ajusta el ancho de la imagen al de la pantalla y, en caso contrario, se ajusta la altura de la imagen a la altura de la pantalla.

public class Pintura extends View{

   int indiceImagen=R.drawable.jerry_garcia1000;

   Drawable imagen=getResources().getDrawable(indiceImagen);

   public Pintura(Context context) {

       super(context);

   }

   @Override

   public void onDraw(Canvas canvas){

     Paint p=new Paint();

     p.setColor(Color.WHITE);

     canvas.drawPaint(p);

     int bottom=getBottom();

     int right=getRight();

     float ratioScreen=(float) bottom/right;

     int anchuraIm= imagen.getIntrinsicWidth();

     int alturaIm= imagen.getIntrinsicHeight();

     float ratio= (float) alturaIm/anchuraIm;

     int anchura,altura;

     // ajusta el ancho

     if(ratio<ratioScreen){

         anchura=right;

         altura= (int) (anchura*ratio);

     } else {

         altura=bottom;

         anchura=(int)(altura/ratio);

     }

     imagen.setBounds(0,0,anchura,altura);

     imagen.draw(canvas);

     p.setColor(Color.RED);

     p.setTextSize(30);

     p.setShadowLayer (1, -3, 2, Color.BLACK);

     canvas.drawText(""+anchuraIm+"x"+alturaIm+" ratio:"+

                                       ratio,  50, 50, p);

     canvas.drawText(""+right+"x"+bottom+" ratio:"+

                                     ratioScreen,50,90,p);

     canvas.drawText(""+anchura+"x"+altura,50,130,p);

   }

  }

}

El resultado se ve en la figura 10.7. Vemos también, en este ejemplo, que se puede dibujar o escribir sobre la imagen como cualquier otro objeto gráfico. En este caso, escribimos en rojo las dimensiones de la imagen, de la pantalla y de la imagen reescalada. Ilustramos también el uso del método setShadowLayer(blur, dx,dy,Color) de la clase Paint, que permite dibujar la sombra, en este caso del texto, para mejorar su contraste con la imagen de fondo. En la figura 10.8 vemos cómo se ajusta el alto automáticamente, en lugar del ancho, al girar el dispositivo (Ctrl-F11 en el emulador).

Figura 10.7. Ajustar una imagen en la pantalla y  escribir texto sombreado encima.

Figura 10.8. Ajustar una imagen en la pantalla en modo horizontal.

Ir a la siguiente página

Report Page