Java 运用Icon (版本2)
Java 使用Icon (版本2)
发展了一下,第2版 做了些自己想要的效果:
package com.han; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.event.ActionEvent; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.HashMap; import java.util.List; import javax.imageio.ImageIO; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JToolBar; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.UIManager.LookAndFeelInfo; import javax.swing.border.Border; import javax.swing.border.CompoundBorder; import javax.swing.border.LineBorder; import javax.swing.plaf.BorderUIResource; import javax.swing.plaf.ColorUIResource; import javax.swing.plaf.basic.BasicBorders; import javax.swing.plaf.metal.DefaultMetalTheme; import javax.swing.plaf.metal.MetalLookAndFeel; import javax.swing.plaf.metal.MetalTheme; import javax.swing.plaf.metal.OceanTheme; import javax.swing.UnsupportedLookAndFeelException; /** * If the graphic files are loaded from an initial thread, there may be a delay * before the GUI appears. If the graphic files are loaded from the event * dispatch thread, the GUI may be temporarily unresponsive. So we use * SwingWorker as a background processing for the loading of image files. * <p> * This application needs the customization of the image files' informations in * the private inner class "LoadImages". * <p> * This is the version 2.0 with the following improvements: * <ul> * <li>Optimization of certain codes. * <li>Adding the choice of several themes. * <li><font size="3" color="red"><b>Creating a Custom Icon * Implementation</b></font> * <p> * The {@code createImage} method returns null when it cannot find an image, but * what should the program do then? One possibility would be to ignore that * image and move on. Another option would be to provide some sort of default * icon to display when the real one cannot be loaded. Making another call to * {@code createImage} might result in another null so using that is not a good * idea. Instead let's create a custom {@code Icon} implementation. * </ul> * * @author HAN * @version 2.0 * @see IconDemoAPP version 1.0 */ @SuppressWarnings("serial") public class IconDemoAPP2 extends JPanel { private static JFrame frame; private JLabel photoLabel; private JToolBar toolBar; private int displayZone = 400; /** * The constructor serves as the content pane construction. */ IconDemoAPP2() { // JPanel uses FlowLayout by default. We set it to BorderLayout for // use of tool bar. This JPanel will be used as content pane. setLayout(new BorderLayout()); // Create and add the tool bar to the content pane toolBar = createToolBar(); add(toolBar, BorderLayout.SOUTH); // Create the photoLabel. photoLabel = new JLabel(); photoLabel.setHorizontalAlignment(SwingConstants.CENTER); photoLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); // Create a scroll pane to contain the photoLabel and set it up to the // center of the content pane in order to display the photo we wanted. JScrollPane scrollPane = new JScrollPane(photoLabel); // I find the border of the scroll pane is ugly, so I simply delete it. scrollPane.setBorder(null); add(scrollPane, BorderLayout.CENTER); // Because at the moment the GUI appearing on screen, the is no content // in the center of the content pane, we have to initialize a size to // display. setPreferredSize(new Dimension(displayZone + 100, displayZone + 50)); // start a SwingWorker to load images in a background thread. new LoadImages().execute(); } private JToolBar createToolBar() { JToolBar toolBar = new JToolBar("Select an icon to be displayed"); // Add two glue components in order to center the icon buttons. toolBar.add(Box.createGlue()); toolBar.add(Box.createGlue()); return toolBar; } /** * @param path * - the path used to create the buffered image. * @return an BufferedImage object, or <code>null</code> if the given path * is not valid or an error occurs during reading. */ private BufferedImage createImage(String path) { URL imageURL = getClass().getResource(path); if (imageURL != null) { try { return ImageIO.read(imageURL); } catch (IOException e) { System.err.println("an error occurs during reading."); e.printStackTrace(); return null; } } else { System.err.println("Couldn't find file: " + path); return null; } } /** * It is for the image icon without the description set by developer. If the * image has a "comment" property that is a string, then the string is used * as the description of this icon. * <p> * Create an icon from an original image, which has normally a bigger size. * * @param image * - the original image to be converted to icon * @param width * - the created icon width * @param height * - the created icon height * @return an Icon object */ private Icon createIcon(Image image, int width, int height) { return createIcon(image, width, height, null); } /** * It is for the image icon that needs a description for the visually * impaired user. * <p> * Create an icon from an original image, which has normally a bigger size. * * @param image * - the original image to be converted to icon * @param width * - the created icon width * @param height * - the created icon height * @param desc * - the description for created icon, which would allow * assistive technologies to help visually impaired user * understand what information the icon conveys. * @return an Icon object */ private Icon createIcon(Image image, int width, int height, String desc) { BufferedImage iconImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = iconImage.createGraphics(); g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2.drawImage(image, 0, 0, width, height, null); g2.dispose();// dispose() is together with create(). if (desc == null) { // If the image has a "comment" property that is a string, then the // string is used as the description of this icon. return new ImageIcon(iconImage); } else { // Return the image icon with specified description set by us. return new ImageIcon(iconImage, desc); } } /** * Based on the original big image, we create a scaled version (keep the * initial width-height ratio) to display if this image is bigger than the * display zone we customized; or else, display it directly. * * @param photoPath * - the path of original big image. */ private void displayPhoto(String photoPath) { BufferedImage photo = createImage(photoPath); if (photo == null) { photoLabel.setIcon(new MissingIcon()); } else { int width = photo.getWidth(); int height = photo.getHeight(); int maxLength = Math.max(width, height); int displayZone = getDisplayZone(); if (maxLength < displayZone) { // display the photo directly photoLabel.setIcon(new ImageIcon(photo)); } else { // display the scaled version (keep the initial width-height // ratio). if (maxLength == photo.getWidth()) { // The width is bigger than the height. photoLabel .setIcon(createIcon( photo, displayZone, (int) (displayZone * ((float) height / (float) width)))); } else { // The height is bigger than the width. photoLabel .setIcon(createIcon( photo, (int) (displayZone * ((float) width / (float) height)), displayZone)); } } } } @SuppressWarnings("unused") private void setDisplayZone(int displayZone) { this.displayZone = displayZone; } private int getDisplayZone() { return displayZone; } @SuppressWarnings("unused") private static void setTheme(String laf) { setTheme(laf, null); } private static void setTheme(String lafName, String theme) { LookAndFeelInfo[] lafInfos = UIManager.getInstalledLookAndFeels(); HashMap<String, String> lafs = new HashMap<String, String>(); for (int i = 0; i < lafInfos.length; i++) { lafs.put(lafInfos[i].getName(), lafInfos[i].getClassName()); } if (lafs.containsKey(lafName)) { // the input L&F name is valid and contained in the installed L&Fs. if (lafName.equals("Metal")) { if (theme == null) { try { UIManager.setLookAndFeel(lafs.get(lafName)); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { e.printStackTrace(); } } else { if (theme.equals("DefaultMetal")) { setMetalTheme(new DefaultMetalTheme()); } else if (theme.equals("Ocean")) { setMetalTheme(new OceanTheme()); } else if (theme.equals("Aqua")) { setMetalTheme(new AquaTheme()); } else if (theme.equals("Charcoal")) { setMetalTheme(new CharcoalTheme()); } else if (theme.equals("Contrast")) { setMetalTheme(new ContrastTheme()); } else if (theme.equals("Emerald")) { setMetalTheme(new EmeraldTheme()); } else if (theme.equals("Ruby")) { setMetalTheme(new RubyTheme()); } else { System.err.println("Your input theme name for the " + "Metal L&F is invalid."); } } } else { try { UIManager.setLookAndFeel(lafs.get(lafName)); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { e.printStackTrace(); } if (theme != null) { System.err.println("The " + lafName + " L&F does not contain acctually any theme. " + "Acctually for this application the only L&F " + "that has the created themes is Metal L&F."); } } } else { // the input L&F name is invalid. System.err.println("Your input L&F name is invalid. And the " + "application will use the default system L&F."); } } /** * Set the theme used by MetalLookAndFeel. * <p> * After the theme is set, MetalLookAndFeel needs to be re-installed. * <p> * If this is not done the results are undefined. * * @param metalTheme * - the metal theme to be set. */ private static void setMetalTheme(MetalTheme metalTheme) { MetalLookAndFeel.setCurrentTheme(metalTheme); // re-install the Metal Look and Feel try { UIManager.setLookAndFeel(new MetalLookAndFeel()); } catch (UnsupportedLookAndFeelException e) { e.printStackTrace(); } } private class LoadImages extends SwingWorker<Void, Icon> { private String imageDir = "/images/IconDemo/"; private String[] imageNames = { "Chrysanthemum.jpg", "Desert.jpg", "Hydrangeas.jpg", "Jellyfish.jpg", "Koala.jpg" }; private String[] imageCaptions = { "Chrysanthemum", "Desert", "Hydrangeas", "Jellyfish", "Koala" }; private Icon buttonIcon; private int iconIndex = 0; @Override protected Void doInBackground() throws Exception { System.out.println("We are now in the Swing's predefined thread: " + "The background thread..."); for (int i = 0; i < imageNames.length; i++) { BufferedImage image = createImage(imageDir + imageNames[i]); if (image == null) { buttonIcon = new MissingIcon(); } else { buttonIcon = createIcon(image, 32, 32, imageCaptions[i]); } // Multiple invocations to the publish method might occur before // the process method is executed. For performance purposes all // these invocations are coalesced into one invocation with // concatenated arguments. And the process method might be // executed many times, which is invoked asynchronously from the // event-dispatching thread. publish(buttonIcon); } return null; } @Override protected void process(List<Icon> iconChunks) { for (Icon buttonIcon : iconChunks) { JButton toolBarButton = new JButton(new ToolBarButtonAction( buttonIcon, imageDir + imageNames[iconIndex], imageCaptions[iconIndex])); iconIndex++; // Add the new button just BEFORE the last glue, which centers // the buttons in the tool bar. toolBar.add(toolBarButton, toolBar.getComponentCount() - 1); } } @Override protected void done() { // After the change of bound properties, you might have to // revalidate the related component for the right layout, by using // JComponent.revalidate() method, or simply call frame.pack(). // toolBar.revalidate(); // frame.pack(); } } private class ToolBarButtonAction extends AbstractAction { ToolBarButtonAction(Icon buttonIcon, String actionCommand, String toolTip) { putValue(LARGE_ICON_KEY, buttonIcon); putValue(ACTION_COMMAND_KEY, actionCommand); putValue(SHORT_DESCRIPTION, toolTip); } @Override public void actionPerformed(ActionEvent e) { // The action command represents the path of the image we want to // display in the center of the content pane. String path = e.getActionCommand(); // Show the photo in the main area. displayPhoto(path); // Set the application title. frame.setTitle("Icon demo: " + new File(path).getName()); } } /** * For thread safety, this method should be invoked from the * event-dispatching thread. */ private static void createAndShowGUI() { // Create the window. frame = new JFrame("Icon demo: Please select an image"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Create and set up the content pane. JPanel contentPane = new IconDemoAPP2(); frame.setContentPane(contentPane); // realize the inside components. frame.pack(); // this centers the frame on the screen frame.setLocationRelativeTo(null); // display the window. frame.setVisible(true); } public static void main(String[] args) { if (!SwingUtilities.isEventDispatchThread()) { System.out.println("This is in the initial thread..."); } // You can shift between the L&Fs. In my computer environment, for // example, the installed L&Fs are: Metal, Nimbus, CDE/Motif, Windows // and Windows Classic. In the Metal L&F, there are some themes: // DefaultMetal, Ocean, Aqua, Charcoal, Contrast, Emerald and Ruby. So // you can use setTheme("Metal");, setTheme("Nimbus");, or // setTheme("Metal", "Aqua");. setTheme("Metal", "Charcoal"); // Schedule a job for the event-dispatching thread: // creating and showing this application's GUI. SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (SwingUtilities.isEventDispatchThread()) { System.out.println("This is in the event-dispatching " + "thread..."); } createAndShowGUI(); } }); } } /** * Create a placeholder icon, which consists in a white box with a black border * and a red x inside. It's used to display something when there are issues * loading an icon from an external location. * * @author HAN * */ class MissingIcon implements Icon { private int width = 32; private int height = 32; @Override public void paintIcon(Component c, Graphics g, int x, int y) { // TODO Auto-generated method stub Graphics2D g2 = (Graphics2D) g; Shape rect = new Rectangle2D.Double(x + 1, y + 1, width - 2, height - 2); g2.setColor(Color.WHITE); g2.fill(rect); g2.setColor(Color.BLACK); g2.draw(rect);// By default, the stroke is 1.0f solid line. g2.setColor(Color.RED); BasicStroke stroke = new BasicStroke(4.0f); g2.setStroke(stroke); g2.draw(new Line2D.Double(x + 10, y + 10, x + width - 10, y + height - 10)); g2.draw(new Line2D.Double(x + 10, y + height - 10, x + width - 10, y + 10)); } @Override public int getIconWidth() { // TODO Auto-generated method stub return width; } @Override public int getIconHeight() { // TODO Auto-generated method stub return height; } } /** * This class describes a theme using "blue-green" colors. */ class AquaTheme extends DefaultMetalTheme { public String getName() { return "Aqua"; } private final ColorUIResource primary1 = new ColorUIResource(102, 153, 153); private final ColorUIResource primary2 = new ColorUIResource(128, 192, 192); private final ColorUIResource primary3 = new ColorUIResource(159, 235, 235); protected ColorUIResource getPrimary1() { return primary1; } protected ColorUIResource getPrimary2() { return primary2; } protected ColorUIResource getPrimary3() { return primary3; } } /** * This class describes a theme using gray colors. */ class CharcoalTheme extends DefaultMetalTheme { public String getName() { return "Charcoal"; } private final ColorUIResource primary1 = new ColorUIResource(66, 33, 66); private final ColorUIResource primary2 = new ColorUIResource(90, 86, 99); private final ColorUIResource primary3 = new ColorUIResource(99, 99, 99); private final ColorUIResource secondary1 = new ColorUIResource(0, 0, 0); private final ColorUIResource secondary2 = new ColorUIResource(51, 51, 51); private final ColorUIResource secondary3 = new ColorUIResource(102, 102, 102); private final ColorUIResource black = new ColorUIResource(222, 222, 222); private final ColorUIResource white = new ColorUIResource(0, 0, 0); protected ColorUIResource getPrimary1() { return primary1; } protected ColorUIResource getPrimary2() { return primary2; } protected ColorUIResource getPrimary3() { return primary3; } protected ColorUIResource getSecondary1() { return secondary1; } protected ColorUIResource getSecondary2() { return secondary2; } protected ColorUIResource getSecondary3() { return secondary3; } protected ColorUIResource getBlack() { return black; } protected ColorUIResource getWhite() { return white; } } /** * This class describes a higher-contrast Metal Theme. */ class ContrastTheme extends DefaultMetalTheme { public String getName() { return "Contrast"; } private final ColorUIResource primary1 = new ColorUIResource(0, 0, 0); private final ColorUIResource primary2 = new ColorUIResource(204, 204, 204); private final ColorUIResource primary3 = new ColorUIResource(255, 255, 255); private final ColorUIResource primaryHighlight = new ColorUIResource(102, 102, 102); private final ColorUIResource secondary2 = new ColorUIResource(204, 204, 204); private final ColorUIResource secondary3 = new ColorUIResource(255, 255, 255); protected ColorUIResource getPrimary1() { return primary1; } protected ColorUIResource getPrimary2() { return primary2; } protected ColorUIResource getPrimary3() { return primary3; } public ColorUIResource getPrimaryControlHighlight() { return primaryHighlight; } protected ColorUIResource getSecondary2() { return secondary2; } protected ColorUIResource getSecondary3() { return secondary3; } public ColorUIResource getControlHighlight() { return super.getSecondary3(); } public ColorUIResource getFocusColor() { return getBlack(); } public ColorUIResource getTextHighlightColor() { return getBlack(); } public ColorUIResource getHighlightedTextColor() { return getWhite(); } public ColorUIResource getMenuSelectedBackground() { return getBlack(); } public ColorUIResource getMenuSelectedForeground() { return getWhite(); } public ColorUIResource getAcceleratorForeground() { return getBlack(); } public ColorUIResource getAcceleratorSelectedForeground() { return getWhite(); } public void addCustomEntriesToTable(UIDefaults table) { Border blackLineBorder = new BorderUIResource( new LineBorder(getBlack())); Object textBorder = new BorderUIResource(new CompoundBorder( blackLineBorder, new BasicBorders.MarginBorder())); table.put("ToolTip.border", blackLineBorder); table.put("TitledBorder.border", blackLineBorder); table.put("TextField.border", textBorder); table.put("PasswordField.border", textBorder); table.put("TextArea.border", textBorder); table.put("TextPane.border", textBorder); table.put("EditorPane.border", textBorder); } } /** * This class describes a theme using glowing green colors. */ class EmeraldTheme extends DefaultMetalTheme { public String getName() { return "Emerald"; } private final ColorUIResource primary1 = new ColorUIResource(51, 142, 71); private final ColorUIResource primary2 = new ColorUIResource(102, 193, 122); private final ColorUIResource primary3 = new ColorUIResource(153, 244, 173); protected ColorUIResource getPrimary1() { return primary1; } protected ColorUIResource getPrimary2() { return primary2; } protected ColorUIResource getPrimary3() { return primary3; } } /** * This class describes a theme using red colors. */ class RubyTheme extends DefaultMetalTheme { public String getName() { return "Ruby"; } private final ColorUIResource primary1 = new ColorUIResource(80, 10, 22); private final ColorUIResource primary2 = new ColorUIResource(193, 10, 44); private final ColorUIResource primary3 = new ColorUIResource(244, 10, 66); protected ColorUIResource getPrimary1() { return primary1; } protected ColorUIResource getPrimary2() { return primary2; } protected ColorUIResource getPrimary3() { return primary3; } }