jueves, 27 de octubre de 2011

How to: Change JInternalFrame Icons // Cambiar iconos de los JInternalFrame (maximizar, cerrar, minimizar)

Hola a todos :D en esta ocasión vamos a ver como darle un aspecto un poco mas personalizado a nuestras aplicaciones swing en java. el objetivo de esta entrada es cambiar los botones de maximiza, minimizar y cerrar(en sus diferentes estados) de los JInternalFrames. He optado por usar la clase NimbusLookAndFeel para lograr el objetivo, lo hago de estar forma porque es un LookAndFeel bastante agradable a la vista y permite (como vimos en esta entrada) poner, de una forma super sencilla, barras traslucidas a los JInternalFrames.
Pasamos de tener algo como esto:


a algo aun mas bonito y personalizado(me refiero al aspecto de los botones de los JInternalFrames x)... por alguna duda que pueda surgir) como esto:

Como vamos a lograr eso?. la verdad super sencillo. algo asi:

Bueno antes de nada, una pequeña y breve introducción de lo que vamos a hacer.
Para empezar debemos tener en cuenta el estado que cada uno de los botones tiene, minimizar y cerrar comparten los mismo estados, los cuales son:

  • Pressed : cuando hacemos y sostenemos el click sobre el botón.
  • Enabled : este estado lo gana cuando la ventana que lo contiene gana el foco.
  • Enabled+WindowNotFocused : la ventana no tiene el foco.
  • MouseOver : el puntero esta sobre el área del botón.
  • MouseOver+WindowNotFocused : el puntero esta sobre el área del botón en una ventana inactiva (no poseedora del foco).
Mientras tanto Maximizar posee esos mismos estado pero agregando algunos mas propios de el, esos estados son :
  • Enabled+WindowMaximized : el botón maximizar pasa a ese estado cuando la ventana contenedora regresa true a la llamada del método isMaximum(), en otras palabras, si esta maximizada pero con el foco ganado.
  • MouseOver+WindowMaximized : el puntero esta sobre el área del botón y ademas la ventana esta maximizada.
  • Pressed+WindowMaximized : esta presionado y ventana padre maximizada.
  • MouseOver+WindowNotFocused : puntero sobre el botón con la condición que la ventana padre no sea poseedora del foco.
  • MouseOver+WindowMaximized+WindowNotFocused : bueno... este es una gran combinación de eventos: puntero sobre el botón con las condiciones que la ventana este maximizada pero no tenga el foco.
Cada botón tiene algunos otros estados, pero creo que con los mencionados el objetivo de dar a entender el funcionamiento se cubre perfectamente; si quieres agregar mas estados, los pasos básicamente serian igual :D
Primero crearemos la clase principal, la cual constara unicamente de una barra de menú y un jdesktopPane.
(si notan alguna similitud con el código de la entrada anterior es porque es el mismo código ^^, almenos esta clase)


import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import javax.swing.JDesktopPane;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.UIManager;

/**
 *
 * @author McCubo
 */
public class LaFMain extends javax.swing.JFrame {

    private JMenuBar menu;
    private JMenu action;
    private JMenuItem actionItem;
    private static JDesktopPane jDesktopPane;

    public LaFMain() {
        initComponents();
        setVisible(true);
    }

    private void initComponents() {
        menu = new JMenuBar();
        action = new JMenu("Action");
        actionItem = new JMenuItem("perform Action");
        jDesktopPane = new JDesktopPane();
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(600, 400);
        setLayout(new BorderLayout());
        jDesktopPane.setBorder(new BackgroundImage());
        actionItem.addActionListener(getListener());
        action.add(actionItem);
        menu.add(action);
        getContentPane().add(menu, BorderLayout.NORTH);
        getContentPane().add(jDesktopPane, BorderLayout.CENTER);
    }

    private ActionListener getListener() {
        return new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                JIF jIF = new JIF();
                jIF.setVisible(true);
                jDesktopPane.add(jIF);
            }
        };
    }

    public static JDesktopPane getjDesktopPane() {
        return jDesktopPane;
    }

    public static void main(String[] args) {
        new LaFMain();
    }
}

class validador {

    public static HashMap<String, JInternalFrame> jIframes = new HashMap<String, JInternalFrame>();

    public static void addJIframe(String key, JInternalFrame jiframe) {
        jIframes.put(key, jiframe);
    }

    public static JInternalFrame getJInternalFrame(String key) {
        return jIframes.get(key);
    }
}


esta clase la guardamos con el nombre de LaFMain.java.
Ahora, creamos una clase que extienda de com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel a esa clase la he llamado McLaFNimbus.java, esa clase solo tendrá 2 métodos y un constructor vació. y su código es el siguente:

import com.sun.java.swing.Painter;
import com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel;
import java.awt.BasicStroke;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.UIDefaults;

/**
 *
 * @author McCubo
 */
public class McLaFNimbus extends NimbusLookAndFeel {

    public McLaFNimbus() {
        super();
    }

    @Override
    public UIDefaults getDefaults() {
        UIDefaults defaults = super.getDefaults();
        String closePressed = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.closeButton\"[Pressed].backgroundPainter";
        String closeEnable = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.closeButton\"[Enabled].backgroundPainter";
        String closeEnableNotFocused = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.closeButton\"[Enabled+WindowNotFocused].backgroundPainter";
        String closeRollOverNotFocus = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.closeButton\"[MouseOver+WindowNotFocused].backgroundPainter";
        String closeRollOver = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.closeButton\"[MouseOver].backgroundPainter";

        String iconEnable = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.iconifyButton\"[Enabled].backgroundPainter";
        String iconRollOver = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.iconifyButton\"[MouseOver].backgroundPainter";
        String iconPressed = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.iconifyButton\"[Pressed].backgroundPainter";
        String iconRollOverNotFocus = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.iconifyButton\"[MouseOver+WindowNotFocused].backgroundPainter";
        String iconNotFocus = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.iconifyButton\"[Enabled+WindowNotFocused].backgroundPainter";

        String maximizeEnable = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.maximizeButton\"[Enabled].backgroundPainter";
        String maximizeMaxEnable = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.maximizeButton\"[Enabled+WindowMaximized].backgroundPainter";
        String maximizeRollOverFocus = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.maximizeButton\"[MouseOver].backgroundPainter";
        String maximizeMaxRollOverFocus = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.maximizeButton\"[MouseOver+WindowMaximized].backgroundPainter";
        String maximizePressed = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.maximizeButton\"[Pressed].backgroundPainter";
        String maximizeMaxPressed = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.maximizeButton\"[Pressed+WindowMaximized].backgroundPainter";
        String maximizeNotFocus = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.maximizeButton\"[Enabled+WindowNotFocused].backgroundPainter";
        String maximizeRollOverNotFocus = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.maximizeButton\"[MouseOver+WindowNotFocused].backgroundPainter";
        String maximizeMaxNotFocus = "InternalFrame:InternalFrameTitlePane:\"InternalFrameTitlePane.maximizeButton\"[MouseOver+WindowMaximized+WindowNotFocused].backgroundPainter";

        defaults.put(closeEnableNotFocused, getPainter("src/resources/LookAndFeel/closeNotFocused.png"));
        defaults.put(closeRollOver, getPainter("src/resources/LookAndFeel/FrameCloseRoll.png"));
        defaults.put(closeEnable, getPainter("src/resources/LookAndFeel/FrameClose.png"));
        defaults.put(closePressed, getPainter("src/resources/LookAndFeel/FrameClosePush.png"));
        defaults.put(closeRollOverNotFocus, getPainter("src/resources/LookAndFeel/rollOverNotFocus.png"));

        defaults.put(iconEnable, getPainter("src/resources/LookAndFeel/iconEnable.png"));
        defaults.put(iconRollOver, getPainter("src/resources/LookAndFeel/iconRollOverFocus.png"));
        defaults.put(iconPressed, getPainter("src/resources/LookAndFeel/iconPush.png"));
        defaults.put(iconRollOverNotFocus, getPainter("src/resources/LookAndFeel/iconRollOverNotFocus.png"));
        defaults.put(iconNotFocus, getPainter("src/resources/LookAndFeel/iconNotFocus.png"));

        defaults.put(maximizeEnable, getPainter("src/resources/LookAndFeel/FrameMaximize.png"));
        defaults.put(maximizeMaxEnable, getPainter("src/resources/LookAndFeel/FrameRestore.png"));
        defaults.put(maximizeRollOverFocus, getPainter("src/resources/LookAndFeel/maximizeRollOverFocus.png"));
        defaults.put(maximizeMaxRollOverFocus, getPainter("src/resources/LookAndFeel/FrameRestoreMaxRollOver.png"));
        defaults.put(maximizePressed, getPainter("src/resources/LookAndFeel/FrameRestorePressed.png"));
        defaults.put(maximizeMaxPressed, getPainter("src/resources/LookAndFeel/FrameRestoreMaxPressed.png"));
        defaults.put(maximizeNotFocus, getPainter("src/resources/LookAndFeel/maximizeNotFocus.png"));
        defaults.put(maximizeRollOverNotFocus, getPainter("src/resources/LookAndFeel/maximizeRollOverNotFocus.png"));
        defaults.put(maximizeMaxNotFocus, getPainter("src/resources/LookAndFeel/maximizeMaxNotFocus.png"));
        return defaults;
    }

    public Painter<JComponent> getPainter(final String path) {
        return new Painter<JComponent>() {

            public void paint(Graphics2D g, JComponent object, int width, int height) {
                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g.setStroke(new BasicStroke(2f));
                g.drawImage(ImageUtils.getScaledImage(path).getImage(), 0, 0, null);
            }
        };
    }
}
class ImageUtils {

    /**
     *
     * @param URL direccion de la imagen dentro del proyecto
     * @return una Imagen redimensionada a 16 x 16
     */
    public static ImageIcon getScaledImage(String path){
        ImageIcon sourceIcon = new ImageIcon(path);
        ImageIcon scaledIcon = new ImageIcon(sourceIcon.getImage().getScaledInstance(16, 16, Image.SCALE_DEFAULT));
        return scaledIcon;
    }

}
y creamos un pequeño cambio en la clase principal, en el constructor, antes del método initComponents(); agregamos el siguiente código:

        try {
            UIManager.setLookAndFeel(new McLaFNimbus());
        } catch (Exception ex) {
            System.out.println(ex);
        }
Lo corremos y ya!! tenemos iconos personalizados en nuestra aplicación java :D. como siempre el código completo de la aplicación lo pueden encontrar en el enlace de descarga justo al final de la entrada.

PD1. Este método solo funciona con NimbusLookAndFeel, si quieres usar otro Look and Feel (MetalLookAndFeel por ejemplo) es totalmente diferente.
PD2.No se si existe otra forma mas sencilla o con menos vueltas para lograr lo que acá he comentado. de ser asi... compartan el hipervínculo xD.

Bueno eso es todo. espero les guste/funcione/sirva/puedan aprender algo,  y para cualquier duda o sugerencia comenten :D. Si esta entrada o alguna otra del blog te ha servido comparte el enlace del blog para que me ayudes a conquistar el mundo el conocimiento llegue a otras personas que también lo necesitamos. Saludos, nos vemos!!!!

el código fuente completo y demás cosas acá

4 comentarios:

  1. bastante interesante a mi lo ke me interesaria es cambiar el fondo del JDesktopPane me pudieras hechar una manita con eso?

    ResponderEliminar
    Respuestas
    1. Hola Sergio. acá hay una entrada en la cual se toca ese tema, dale una leída y si tienes dudas, pues por acá estamos. http://gnuteam.blogspot.com/2011/09/how-to-jinternalframe-barra-de-titulo.html

      Eliminar
  2. Xq cambia mis Joptionpane perzonalizados en un internalframe

    ResponderEliminar