Personalización y pintado Personalizado
Look and Feel (LaF)
El Look and Feel (LaF) controla el aspecto visual global de todos los componentes. Swing incluye varios LaF y permite usar librerías externas.
// Cambiar LaF antes de crear la interfaz
try {
// LaF del sistema operativo (recomendado para integración nativa)
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
// LaF de Nimbus (moderno, incluido en Java 6+)
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
// LaF Metal (el predeterminado de Swing)
UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
// Aplicar a ventanas ya abiertas:
SwingUtilities.updateComponentTreeUI(ventana);
ventana.pack();
} catch (Exception e) {
e.printStackTrace(); // Usar el LaF por defecto si falla
}
LaF disponibles en Java estándar
// Listar todos los LaF instalados
UIManager.LookAndFeelInfo[] lafs = UIManager.getInstalledLookAndFeels();
for (UIManager.LookAndFeelInfo info : lafs) {
System.out.println(info.getName() + " → " + info.getClassName());
}
Personalizar propiedades de UIManager
Se pueden cambiar colores, fuentes y tamaños globalmente:
UIManager.put("Button.background", new Color(70, 130, 180));
UIManager.put("Button.foreground", Color.WHITE);
UIManager.put("Button.font", new Font("Arial", Font.BOLD, 13));
UIManager.put("Button.border", BorderFactory.createEmptyBorder(6, 14, 6, 14));
UIManager.put("Panel.background", new Color(245, 245, 245));
UIManager.put("Label.font", new Font("Arial", Font.PLAIN, 13));
UIManager.put("TextField.selectionColor", new Color(100, 160, 230));
UIManager.put("Table.gridColor", Color.LIGHT_GRAY);
UIManager.put("Table.alternateRowColor", new Color(240, 248, 255));
Colores y fuentes
// Color
Color rojo = Color.RED;
Color verde = new Color(0, 200, 0); // RGB
Color azul = new Color(0, 0, 255, 180); // RGBA (con transparencia)
Color hex = Color.decode("#3498DB"); // Hexadecimal
Color hsb = Color.getHSBColor(0.6f, 0.8f, 0.9f);
// Mezclar colores
Color mezcla = new Color(
(color1.getRed() + color2.getRed()) / 2,
(color1.getGreen() + color2.getGreen()) / 2,
(color1.getBlue() + color2.getBlue()) / 2
);
// Fuentes
Font arial = new Font("Arial", Font.PLAIN, 14);
Font negrita = new Font("Verdana", Font.BOLD, 16);
Font cursiva = new Font("Georgia", Font.ITALIC, 13);
Font ambos = new Font("Tahoma", Font.BOLD | Font.ITALIC, 14);
Font monospace = new Font(Font.MONOSPACED, Font.PLAIN, 12); // Fuente monoespaciada
// Derivar de una fuente existente
Font base = label.getFont();
Font mayor = base.deriveFont(base.getSize() + 4f);
Font bold = base.deriveFont(Font.BOLD);
Font scaled = base.deriveFont(Font.PLAIN, 20f);
// Cargar fuente desde archivo
try (InputStream is = getClass().getResourceAsStream("/fonts/mifuente.ttf")) {
Font custom = Font.createFont(Font.TRUETYPE_FONT, is).deriveFont(14f);
GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(custom);
label.setFont(custom);
} catch (Exception e) { e.printStackTrace(); }
Pintado personalizado con paintComponent
Para dibujar formas, imágenes o gráficos personalizados se sobreescribe paintComponent en un JPanel (o cualquier JComponent).
public class MiCanvas extends JPanel {
@Override
protected void paintComponent(Graphics g) {
// 1. SIEMPRE llamar al padre primero (limpia el fondo)
super.paintComponent(g);
// 2. Convertir a Graphics2D para más control
Graphics2D g2 = (Graphics2D) g.create(); // .create() para no modificar el original
// 3. Activar antialiasing (bordes suavizados)
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
// 4. Dibujar
dibujar(g2);
// 5. SIEMPRE liberar el contexto al final
g2.dispose();
}
private void dibujar(Graphics2D g2) {
int w = getWidth();
int h = getHeight();
// Fondo degradado
GradientPaint gradiente = new GradientPaint(0, 0, Color.LIGHT_GRAY, 0, h, Color.WHITE);
g2.setPaint(gradiente);
g2.fillRect(0, 0, w, h);
// Rectángulo relleno
g2.setColor(new Color(70, 130, 180));
g2.fillRoundRect(20, 20, 100, 60, 15, 15);
// Borde del rectángulo
g2.setColor(Color.DARK_GRAY);
g2.setStroke(new BasicStroke(2f));
g2.drawRoundRect(20, 20, 100, 60, 15, 15);
// Elipse / Círculo
g2.setColor(new Color(220, 80, 80, 180)); // Con transparencia
g2.fillOval(150, 20, 80, 80);
// Línea con grosor
g2.setColor(Color.BLACK);
g2.setStroke(new BasicStroke(3f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.drawLine(20, 120, 250, 120);
// Línea discontinua
float[] patron = {8f, 4f};
g2.setStroke(new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, patron, 0f));
g2.drawLine(20, 140, 250, 140);
// Texto
g2.setFont(new Font("Arial", Font.BOLD, 16));
g2.setColor(Color.DARK_GRAY);
g2.drawString("Hola, Canvas!", 20, 180);
// Texto centrado
String texto = "Centrado";
FontMetrics fm = g2.getFontMetrics();
int xTexto = (w - fm.stringWidth(texto)) / 2;
int yTexto = (h + fm.getAscent()) / 2;
g2.drawString(texto, xTexto, yTexto);
// Polígono
int[] xs = {50, 100, 150, 130, 70};
int[] ys = {200, 170, 200, 240, 240};
g2.setColor(new Color(100, 200, 100, 150));
g2.fillPolygon(xs, ys, 5);
g2.setColor(Color.GREEN.darker());
g2.setStroke(new BasicStroke(2f));
g2.drawPolygon(xs, ys, 5);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
}
Graphics2D — formas avanzadas con java.awt.geom
import java.awt.geom.*;
// Path2D — trazados complejos
Path2D path = new Path2D.Double();
path.moveTo(100, 200);
path.lineTo(200, 100);
path.curveTo(250, 50, 300, 150, 350, 100); // Curva Bézier cúbica
path.quadTo(400, 200, 450, 150); // Curva Bézier cuadrática
path.closePath();
g2.setColor(new Color(200, 100, 50));
g2.fill(path);
g2.setColor(Color.BLACK);
g2.draw(path);
// Formas predefinidas
Shape circulo = new Ellipse2D.Double(x, y, diametro, diametro);
Shape rectangulo = new Rectangle2D.Double(x, y, ancho, alto);
Shape arco = new Arc2D.Double(x, y, w, h, startAngle, arcAngle, Arc2D.PIE);
Shape linea = new Line2D.Double(x1, y1, x2, y2);
Shape curva = new CubicCurve2D.Double(x1,y1, cx1,cy1, cx2,cy2, x2,y2);
g2.fill(circulo);
g2.draw(arco);
// Transformaciones
g2.translate(100, 100); // Trasladar
g2.rotate(Math.PI / 4); // Rotar (radianes)
g2.scale(1.5, 1.5); // Escalar
g2.shear(0.3, 0); // Cizallar
// Guardar y restaurar transformación
AffineTransform original = g2.getTransform();
g2.rotate(Math.toRadians(45), cx, cy); // Rotar sobre un punto
g2.drawRect(x, y, w, h);
g2.setTransform(original); // Restaurar
Pintado de imágenes
public class PanelImagen extends JPanel {
private BufferedImage imagen;
public PanelImagen() {
try {
imagen = ImageIO.read(getClass().getResource("/images/foto.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (imagen == null) return;
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
// Dibujar imagen en posición y tamaño exacto
g2.drawImage(imagen, x, y, ancho, alto, this);
// Imagen escalada al panel manteniendo proporción
double scaleX = (double) getWidth() / imagen.getWidth();
double scaleY = (double) getHeight() / imagen.getHeight();
double scale = Math.min(scaleX, scaleY); // Escala uniforme
int iw = (int) (imagen.getWidth() * scale);
int ih = (int) (imagen.getHeight() * scale);
int ix = (getWidth() - iw) / 2;
int iy = (getHeight() - ih) / 2;
g2.drawImage(imagen, ix, iy, iw, ih, this);
// Imagen con transparencia (Alpha Composite)
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
g2.drawImage(imagen, 0, 0, this);
g2.dispose();
}
}
Componentes personalizados
Ejemplo completo: botón redondeado con hover effect.
public class BotonModerno extends JButton {
private Color colorBase;
private Color colorHover;
private Color colorActual;
private int radio = 10;
public BotonModerno(String texto, Color colorBase) {
super(texto);
this.colorBase = colorBase;
this.colorHover = colorBase.brighter();
this.colorActual = colorBase;
setContentAreaFilled(false); // Deshabilitar pintado nativo
setFocusPainted(false); // Sin borde de foco nativo
setBorderPainted(false);
setOpaque(false);
setForeground(Color.WHITE);
setFont(new Font("Arial", Font.BOLD, 14));
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
setPreferredSize(new Dimension(120, 36));
addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
colorActual = colorHover;
repaint();
}
@Override
public void mouseExited(MouseEvent e) {
colorActual = colorBase;
repaint();
}
@Override
public void mousePressed(MouseEvent e) {
colorActual = colorBase.darker();
repaint();
}
@Override
public void mouseReleased(MouseEvent e) {
colorActual = colorHover;
repaint();
}
});
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Sombra
g2.setColor(new Color(0, 0, 0, 40));
g2.fillRoundRect(2, 4, getWidth() - 4, getHeight() - 4, radio, radio);
// Fondo del botón
g2.setColor(colorActual);
g2.fillRoundRect(0, 0, getWidth() - 2, getHeight() - 2, radio, radio);
// Texto
g2.setFont(getFont());
g2.setColor(getForeground());
FontMetrics fm = g2.getFontMetrics();
int x = (getWidth() - fm.stringWidth(getText())) / 2;
int y = (getHeight() + fm.getAscent() - fm.getDescent()) / 2;
g2.drawString(getText(), x, y);
g2.dispose();
}
}
Reglas de pintado en Swing
- Siempre llama a
super.paintComponent(g)primero (limpia el área, pinta el fondo). - Usa
g.create()para obtener una copia del contexto yg2.dispose()al acabar. - Activa antialiasing con
RenderingHintspara gráficos suaves. - Nunca llames a
paint()opaintComponent()directamente; usarepaint()para solicitar un redibujado. repaint()encola el redibujado en el EDT; es seguro llamarlo desde cualquier hilo.paintComponentdebe ser rápido: no hagas operaciones costosas aquí; precalcula los datos.