Copy/Paste de imágenes en el Clipboard del sistema

De ChuWiki

Vamos a ver un pequeño ejemplo de cómo copiar y pegar imágenes en el Clipboard del sistema, de forma que podamos copiar imágenes desde una aplicación externa (como por ejemplo el Paint Brush de Windows) y pegarlos en nuestro programa java y al revés. El ejemplo funciona correctamente en Windows con Paint Brush. En linux con el gimp, sólo funciona el botón paste. El botón copy no consigue que gimp pueda pegar la imagen.


El Transferable ImageSelection[editar]

Como comentamos en Uso del Clipboard del sistema es necesario que los datos que metemos y sacamos del Clipboard implementen la interface Transferable. Para textos, java nos proporciona la clase StringSelection, que contiene un String e implementa Transferable. Para imágenes no tenemos una clase parecida, así que debemos hacerla. Para guardar similitud con el nombre, la llamaremos ImageSelection.

Una de las cosas que debe conocer esta clase es el tipo de dato que tiene dentro, es decir, una imágen. Los tipos de datos en el Clipboard se identifican con una clase llamada DataFlavor. Así, hay DataFlavor de texto, de imágenes y de cualquier otro tipo que queramos inventar. Java nos proporciona un DataFlavor para imágenes, que es DataFlavor.imageFlavor, así que usaremos este. En la implementación de Transferable de ImageSelection, diremos que soporta DataFlavor.imageFlavor.

Una implementación de ImageSelection puede ser esta

package com.chuidiang.ejemplos.clipboard.imagen;

import java.awt.Image;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;

/**
 * Implementación de Transferable para imagenes.
 * Este dato ImageSelection implementa Transferable y contiene la imagen.
 * Es el dato que se debe meter en el Clipboard.
 * @author Chuidiang
 *
 */
public class ImageSelection implements Transferable {
	private Image image;

	/**
	 * Se guarda la imagen que se le pasa.
	 * @param image
	 */
	public ImageSelection(Image image) {
		this.image = image;
	}

	/**
	 * Devuelve los DataFlavor soportados, es decir,
	 * DataFlavor.imageFlavor
	 */
	public DataFlavor[] getTransferDataFlavors() {
		return new DataFlavor[] { DataFlavor.imageFlavor };
	}

	/**
	 * Devuelve true si el flavor que se le pasa es DataFlavor.imageFlavor
	 */
	public boolean isDataFlavorSupported(DataFlavor flavor) {
		return DataFlavor.imageFlavor.equals(flavor);
	}

	/**
	 * Devuelve la imagen si el DataFlavor que se le pasa es DataFlavor.imageFlavor
	 */
	public Object getTransferData(DataFlavor flavor)
			throws UnsupportedFlavorException, IOException {
		if (!DataFlavor.imageFlavor.equals(flavor)) {
			throw new UnsupportedFlavorException(flavor);
		}
		return image;
	}
}


Acción copiar imagen en Clipboard[editar]

Como vamos a poner un botón de copiar imagen en el Clipboard y la imagen la cogeremos de un JLabel, hacemos una Action que coja la imagen del JLabel y la ponga en el Clipboard. Esta Action básicamente debe:

  • Recoger la imagen del JLabel
  • Instanciar un ImageSelection y meter la imagen dentro de este ImageSeletion
  • Finalmente, meter la ImageSelection en el Clipboard.

El código para esta Action puede ser este

package com.chuidiang.ejemplos.clipboard.imagen;

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JLabel;

/**
 * Action copy al Clipboard de la imagen que hay en el JLabel que se le
 * pasa.
 * @author Chuidiang
 *
 */
public class ActionCopyImagen extends AbstractAction {
	/** JLabel del que coger la imagen si la hay */
	private JLabel l;

	/**
	 * Guarda el JLabel que se le pasa y le pone nombre a la Action. 
	 * @param l
	 */
	public ActionCopyImagen(JLabel l) {
		this.l = l;
		this.putValue(Action.NAME, "Copy");
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		// Recoge el Clipboard del sistema.
		Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
		
		// Recoge la imagen del JLabel.
		ImageIcon image = (ImageIcon) l.getIcon();
		
		// Construye un Transferable con la imagen
		ImageSelection dh = new ImageSelection(image.getImage());
		
		// Pone la imagen en el Clipboard
		cb.setContents(dh, null);
	}

}

Acción pegar imagen del Clipboard[editar]

Otro botón recoge la imagen que hay en el Clipboard y la pone en el JLabel. Así que haremos una Action que haga todo esto. Esta Action debe:

  • Recoger el dato del Clipboard. Este dato será un Transferable.
  • Preguntarle al Transferable si el tipo de dato es una imagen. Como comentamos antes, el tipo de dato se identifica por un DataFlavor, y por ello, le pregunta si "soporta" un DataFlavor.imageFlavor
  • Si la respuesta es afirmativa, le pide al Transferable el dato que será una Image de java.
  • Pone esta Image en el JLabel.

El código completo de esta Action puede ser como el siguiente

package com.chuidiang.ejemplos.clipboard.imagen;

import java.awt.Image;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JLabel;

/**
 * Action del botón "paste" de imagen. Recoge la imagen del Clipboard del
 * sistema y la peqa en el JLabel que se le pasa en el constructor.
 * @author Chuidiang
 *
 */
public class ActionPasteImagen extends AbstractAction {
	private JLabel l;

	/**
	 * Guarda el JLabel y pone nombre a la Action
	 * @param l El JLabel sobre el que pondrá la imagen.
	 */
	public ActionPasteImagen(JLabel l) {
		this.l = l;
		this.putValue(Action.NAME, "Paste");
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		try {
			// Obtiene el Clipboard del sistema
			Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();

			// Obtiene el contenido
			Transferable t = cb.getContents(null);
                        if (t==null) return;
			
			// Verifica si una imagen. Si es así, la pone en la 
			// etiqueta.
			if (t.isDataFlavorSupported(DataFlavor.imageFlavor)) {
				l.setIcon(new ImageIcon((Image) t
						.getTransferData(DataFlavor.imageFlavor)));
			}

		} catch (Exception exc) {
			exc.printStackTrace();
		}

	}

}


La Action de borrar el JLabel[editar]

Hemos puesto un botón para borrar la imagen del JLabel. Este botón no hace la acción cut ni nada parecido, símplemente se pone para ver mejor los efectos de paste, borrando previamente la imagen presente en el JLabel. No tiene nada que ver con el Clipboard ni con el tema que estamos tratando, pero como está presente en el ejemplo, ponemos aquí el código de la Action.

package com.chuidiang.ejemplos.clipboard.imagen;

import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JLabel;

/**
 * Action que al ejecutarse quita el icono del JLabel que se le pasa. 
 * @author Chuidiang
 *
 */
public class ActionBorraEtiqueta extends AbstractAction {
	/** El JLabel al que borrar el icono */
	private JLabel l;

	/**
	 * Guarda el JLabel y le pone nombre a la Action.
	 * @param l
	 */
	public ActionBorraEtiqueta(JLabel l) {
		this.l = l;
		this.putValue(Action.NAME, "Borrar");
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		// borra el icono del JLabel
		l.setIcon(null);
	}

}


La ventana principal[editar]

Ponemos ahora el código de la ventana principal del ejemplo. Símplemente un JFrame con un JLabel que contendrá la imagen y sufrirá nuestros copy-paste, un par de botones con copy y paste y finalmente el botón de borrar el JLabel.

Este código, junto con las tres Action y el ImageSelection componen el código total del ejemplo. El código de la ventana principal puede ser como el siguiente. Fíjate que hay una línea en que presupone que tienes una imagen penguin.gif junto a tu ejecutable. Cambia este nombre por alguna imagen que tú tengas en fichero.

package com.chuidiang.ejemplos.clipboard.imagen;

import java.awt.BorderLayout;
import java.awt.FlowLayout;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.WindowConstants;

/**
 * Ejemplo de copy-paste de imágenes en el Clipboard del sistema.
 * @author Chuidiang
 *
 */
public class CopyPasteImagen {

	/**
	 * Crea una ventana con un JLabel con una imagen, un botón de copy,
	 * otro de paste y otro de borrar la imagen del JLabel.
	 * @param args Argumentos de línea de comandos.
	 */
	public static void main(String[] args) {
		// Instanciación de los componentes
		JFrame v = new JFrame("Copy Paste imagen");
		JPanel p = new JPanel(new FlowLayout());
		JLabel l = new JLabel(new ImageIcon("penguin.gif"));  // cambia esta imagen
		JButton bcopy = new JButton(new ActionCopyImagen(l));
		JButton bpaste = new JButton(new ActionPasteImagen(l));
		JButton bborrar = new JButton(new ActionBorraEtiqueta(l));

		// Construcción de la ventana
		p.add(bcopy);
		p.add(bpaste);
		p.add(bborrar);
		v.getContentPane().add(l, BorderLayout.CENTER);
		v.getContentPane().add(p, BorderLayout.NORTH);
		
		// Visualización de la misma
		v.pack();
		v.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		v.setVisible(true);
	}
}


Artículos relacionados[editar]