发展了一下,第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;
}
}