How to change dynamically the icons when the look and feel change?

江枫思渺然 提交于 2019-12-11 07:50:10

问题


I'm developing a look-and-feel with two themes.

The problem is:

  • I want to be able to switch dynamically between the two themes (it means changing the themes after startup).
  • But the themes have two different sets of icons (in fact the same icons with different colors).

I don't know how to change dynamically the icons in the entire application.

One solution would be to register each component with an icon and the icon ID on an icon manager to switch between the two kinds of icons, but it seems a very heavy solution!


回答1:


There might be a lot of different approaches (and i cannot say which one is the best):

  1. Create your own icon class based on javax.swing.Icon and paint there an actual icon based on currently installed L&F
  2. Setup component icons inside the installUI method since it is called when component UI is installed OR reinstalled (for example when L&F is changed)
  3. Create your own components or their UIs that handles icon painting
  4. Override components which uses such icons and replace icon retrieval methods

There might be more, but those are first that comes to mind...

Here is the 1st solution working example:

import com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel;

import javax.swing.*;
import javax.swing.plaf.metal.MetalLookAndFeel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * @see http://stackoverflow.com/a/12301173/909085
 */

public class LafIcon implements Icon
{
    private Map<String, Icon> lafIcons;

    public LafIcon ()
    {
        super ();
        lafIcons = new HashMap<String, Icon> ();
    }

    public void addIcon ( String laf, Icon icon )
    {
        lafIcons.put ( laf, icon );
    }

    private String getLaf ()
    {
        return UIManager.getLookAndFeel ().getClass ().getCanonicalName ();
    }

    private Icon getCurrentIcon ()
    {
        return lafIcons.get ( getLaf () );
    }

    public void paintIcon ( Component c, Graphics g, int x, int y )
    {
        Icon icon = getCurrentIcon ();
        if ( icon != null )
        {
            icon.paintIcon ( c, g, x, y );
        }
    }

    public int getIconWidth ()
    {
        Icon icon = getCurrentIcon ();
        return icon != null ? icon.getIconWidth () : 0;
    }

    public int getIconHeight ()
    {
        Icon icon = getCurrentIcon ();
        return icon != null ? icon.getIconHeight () : 0;
    }

    public static void main ( String[] args )
    {
        installMetalLookAndFeel ();

        JFrame frame = new JFrame ();
        frame.setLayout ( new FlowLayout ( FlowLayout.CENTER, 5, 5 ) );

        frame.add ( new JButton ( "Test button", createIcon () ) );

        String[] laf = { "Metal Look and Feel", "Nimbus Look and Feel" };
        final JComboBox lafType = new JComboBox ( laf );
        lafType.addActionListener ( new ActionListener ()
        {
            public void actionPerformed ( ActionEvent e )
            {
                if ( lafType.getSelectedIndex () == 0 )
                {
                    installMetalLookAndFeel ();
                }
                else
                {
                    installNimbusLookAndFeel ();
                }
            }
        } );
        frame.add ( lafType );

        frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
        frame.pack ();
        frame.setLocationRelativeTo ( null );
        frame.setVisible ( true );
    }

    private static LafIcon createIcon ()
    {
        LafIcon icon = new LafIcon ();    
        try
        {
            icon.addIcon ( MetalLookAndFeel.class.getCanonicalName (), new ImageIcon (
                    new URL ("http://cdn3.iconfinder.com/data/icons/fatcow/32x32_0020/application_form.png") ) );
            icon.addIcon ( NimbusLookAndFeel.class.getCanonicalName (), new ImageIcon (
                    new URL ("http://cdn3.iconfinder.com/data/icons/fatcow/32x32_0040/application_view_gallery.png") ) );
        }
        catch ( MalformedURLException e )
        {
            e.printStackTrace ();
        }    
        return icon;
    }

    private static void installMetalLookAndFeel ()
    {
        installLookAndFeel ( MetalLookAndFeel.class.getCanonicalName () );
    }

    private static void installNimbusLookAndFeel ()
    {
        installLookAndFeel ( NimbusLookAndFeel.class.getCanonicalName () );
    }

    private static void installLookAndFeel ( String name )
    {
        try
        {
            UIManager.setLookAndFeel ( name );

            Window[] windows = Window.getWindows ();
            if ( windows.length > 0 )
            {
                for ( Window window : windows )
                {
                    SwingUtilities.updateComponentTreeUI ( window );
                    window.pack ();
                }
            }
        }
        catch ( ClassNotFoundException e )
        {
            e.printStackTrace ();
        }
        catch ( InstantiationException e )
        {
            e.printStackTrace ();
        }
        catch ( IllegalAccessException e )
        {
            e.printStackTrace ();
        }
        catch ( UnsupportedLookAndFeelException e )
        {
            e.printStackTrace ();
        }
    }
}

Actually, as you can see, most part of the code is the example. You can modify the icon code to provide additional icons add/set/remove methods to make the icon much more simple to create.

Also you don't need to listen to L&F changes using this way, since on component UI change it will be repainted and revalidated so the icon size and paint methods will get called again and will provide the new icon for updated L&F.



来源:https://stackoverflow.com/questions/12266196/how-to-change-dynamically-the-icons-when-the-look-and-feel-change

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!