PDA

View Full Version : [Java - Trick(eballacche)] Icone e Layout svg


PGI-Bis
19-03-2012, 14:15
Non che non abbia voglia di fare quello che devo e quindi io stia qui a far ballare la scimmia MA condivido due trucconi che ultimamente mi capita di usare spesso.

Libreria: http://svgsalamander.java.net/

Truccone numero uno: icone.

Pigliate un editor tipo inkscape, è facile disegnare icone. Esportarle, ridimensionarle, impacchettarle... è una palla tremenda. Che ti fa il bradiprogrammatore? Mi usa direttamente il file svg. Dato l'url di un file svg un'istanza della classe UIIcons che segue genera sottoimmagini:

package it.tukano.utilities.ui;

import com.kitfox.svg.RenderableElement;
import com.kitfox.svg.SVGCache;
import com.kitfox.svg.SVGDiagram;
import com.kitfox.svg.SVGElement;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.net.URI;
import java.net.URL;
import javax.swing.ImageIcon;

/**
* Generates a pack of icons from a svg document
* @author utente4
*/
public class UIIcons {
private final SVGDiagram diagram;

public UIIcons(URL svgfile) {
URI uri = SVGCache.getSVGUniverse().loadSVG(svgfile);
diagram = SVGCache.getSVGUniverse().getDiagram(uri);
}

public ImageIcon getIcon(String id, Dimension size) {
BufferedImage image = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_ARGB);
SVGElement element = diagram.getElement(id);
if(element == null) element = diagram.getElement(id.toLowerCase());
if(element == null) element = diagram.getElement(id.toUpperCase());
if(element instanceof RenderableElement) try {
RenderableElement re = (RenderableElement) element;
Rectangle bounds = re.getBoundingBox().getBounds();
double scaleX = size.getWidth() / bounds.getWidth();
double scaleY = size.getHeight() / bounds.getHeight();
Graphics2D graphics = image.createGraphics();
graphics.scale(scaleX, scaleY);
graphics.translate(-bounds.getX(), -bounds.getY());
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
re.render(graphics);
} catch(Exception ignore) {}
return new ImageIcon(image);
}
}

Uso:

UIIcons icons = new UIIcons(someclass.getResource("pippo.svg"));
Icon salva24 = icons.getIcon("salva", new Dimension(24, 24));
Icon salva16 = icons.getIcon("salva", new Dimension(16, 16));

I "nomi" delle icone sono gli ID degli elementi SVG (di solito un gruppo, con inkscape si associano id con "proprietà oggetto") a cui corrispondono le immagini.

Truccone 2: layout.

Se si possono prendere immagini allora si possono anche prendere delle aree a con le aree si possono fare dei layout manager.

package it.tukano.utilities.ui;

import com.kitfox.svg.*;
import java.awt.*;
import java.net.URI;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

public class SvgLayoutManager implements LayoutManager {

private final Map<Component, Rectangle> boundsMap = new HashMap<>();
private final SVGDiagram diagram;
private final boolean useSvgWidth;

public SvgLayoutManager(String resource) {
this(SvgLayoutManager.class.getResource(resource), false);
}

/**
* Instance initializer
*/
public SvgLayoutManager(URL svguri, boolean useSvgWidth) {
URI uri = SVGCache.getSVGUniverse().loadSVG(svguri);
diagram = SVGCache.getSVGUniverse().getDiagram(uri);
this.useSvgWidth = useSvgWidth;
}

public SvgLayoutManager(URL svguri) {
this(svguri, false);
}

@Override
public void addLayoutComponent(String name, Component comp) {
SVGElement element = findDiagramElement(name);
if(element instanceof RenderableElement) {
RenderableElement re = (RenderableElement) element;
try {
Rectangle bounds = re.getBoundingBox().getBounds();
if(bounds != null && name != null) {
boundsMap.put(comp, bounds);
} else {
Logger.getLogger(SvgLayoutManager.class.getName()).log(Level.INFO, "NO BOUNDS FOR COMPONENT " + name);
}
} catch (SVGException ex) {
Logger.getLogger(SvgLayoutManager.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
Logger.getLogger(SvgLayoutManager.class.getName()).log(Level.INFO, "NOT RENDERABLE " + element);
}
}

@Override
public void removeLayoutComponent(Component comp) {
boundsMap.remove(comp);
}

@Override
public Dimension preferredLayoutSize(Container parent) {
return minimumLayoutSize(parent);
}

@Override
public Dimension minimumLayoutSize(Container parent) {
synchronized(parent.getTreeLock()) {
final Insets insets = parent.getInsets();
int w = 0;
int h = 0;
for (Rectangle rectangle : boundsMap.values()) {
w = Math.max(w, rectangle.x + rectangle.width);
h = Math.max(h, rectangle.y + rectangle.height);
}
if(useSvgWidth) {
w = (int) diagram.getWidth();
}
w += insets.left + insets.right;
h += insets.top + insets.bottom;
return new Dimension(w, h);
}
}

@Override
public void layoutContainer(Container parent) {
synchronized (parent.getTreeLock()) {
final Insets insets = parent.getInsets();
final int x = insets.left;
final int y = insets.top;
for (Component component : parent.getComponents()) {
Rectangle bounds = boundsMap.get(component);
if(bounds != null) {
component.setBounds(x + bounds.x, y + bounds.y, bounds.width, bounds.height);
}
}
}
}

private SVGElement findDiagramElement(String name) {
SVGElement child = diagram.getElement(name);
if(child == null) child = diagram.getElement(name.toLowerCase());
if(child == null) child = diagram.getElement(name.toUpperCase());
return child;
}
}

Che fa 'sta cosa. Dato un documento svg lo usa come una mappa nome-area rettangolare. Il nome è l'id di un qualsiasi elemento del documento svg.

Quando dico:

JPanel p = new JPanel(new SVGLayoutManager(url documento svg));
p.add("pulsante", new JButton("..:"))

Il layout manager piglia la stringa "pulsante", va a vedere nel documento svg se c'è un elemento di id "pulsante", se c'è calcola l'area di quell'elemento e la associa al pulsante.

Non è esattamente una bella cosa da fare, perchè le aree in questo modo sono fisse - al massimo le si potrebbe fare proporzionali - ma nel contesto in cui l'ho scritto - un programma con uno svalangone di form che oltretutto cambiano un giorno sì e l'altro pure - definire la struttura del form con inkscape è molto più comodo che non farlo con l'editor di form o a mano.

E via a divertirsi di nuovo...

banryu79
19-03-2012, 14:37
Aww... fichissime le icone svg a dimensione variabile on-demand; valanghe di tempo (e frustrazione) risparmiate, grazie PGI-Bis :)
EDIT:
non conosco bene il formato svg, per cui chiedo: nel disegnare l'elemento originale pensando di derivarne poi degli altri ridimensionati è neccessario/consigliabile usare una qualche dimensione specifica (es.: la più grande che si pensa di usare) oppure la questione è assolutamente irrilevante (vector graphic)?

PGI-Bis
19-03-2012, 15:26
E' irrilevante, svg è vettoriale e viene "disegnato" da salamander usando le corrispondenti funzioni di Java2D