问题
I'm working on a card game based on the NetBeans platform and I'm struggling to get my head around dynamic images. Why dynamic? Well I want the cards to adjust at run time to changes to the page (i.e. name, text, cost, etc).
My first hack at it was creating a component (JPanel) with labels pre-placed where I loaded the text/image based on the card values. That seems to work fine but then it became troublesome when I thought about some pages having a different look in later editions (meaning not everything would be on the same place).
So I'm trying to get an idea about ways to do this based on some kind of template.
Any idea?
There's a follow-up question at: JList of cards?
回答1:
Finally I got some time to get back to this and was able to figure out a way using Java 2D tutorial.
The pictures are not near what I will use in my application but serves as proof of concept.
package javaapplication3;
import java.awt.*; import java.awt.font.FontRenderContext; import java.awt.font.LineBreakMeasurer; import java.awt.font.TextAttribute; import java.awt.font.TextLayout; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.text.AttributedCharacterIterator; import java.text.AttributedString; import java.util.ArrayList; import java.util.HashMap; import java.util.logging.Level; import java.util.logging.Logger; import javax.imageio.ImageIO;
/** * * @author Javier A. Ortiz Bultrón */ public class DefaultImageManager {
/** * @param args the command line arguments */ public static void main(String[] args) { try { // TODO code application logic here DefaultImageManager manager = new DefaultImageManager(); URL url = DefaultImageManager.class.getResource("weather-rain.png"); manager.getLayers().add(ImageIO.read(url)); url = DefaultImageManager.class.getResource("weather-sun.png"); manager.getLayers().add(ImageIO.read(url)); manager.addText(new Font("Arial", Font.PLAIN, 10), "Many people believe that Vincent van Gogh painted his best works " + "during the two-year period he spent in Provence. Here is where he " + "painted The Starry Night--which some consider to be his greatest " + "work of all. However, as his artistic brilliance reached new " + "heights in Provence, his physical and mental health plummeted. ", 200, 150, new Point(0, 0)); manager.generate(); } catch (MalformedURLException ex) { Logger.getLogger(DefaultImageManager.class.getName()).log(Level.SEVERE,
null, ex); } catch (IOException ex) { Logger.getLogger(DefaultImageManager.class.getName()).log(Level.SEVERE, null, ex); } } /** * Layers used to create the final image */ private ArrayList layers = new ArrayList(); private ArrayList textLayers = new ArrayList();
/** * @return the layers */ public ArrayList<BufferedImage> getLayers() { return layers; } private Dimension getMaxSize() { int width = 0, height = 0; for (BufferedImage img : getLayers()) { if (img.getWidth() > width) { width = img.getWidth(); } if (img.getHeight() > height) { height = img.getHeight(); } } return new Dimension(width, height); } public void addText(Font font, String text, int height, int width, Point location) { BufferedImage textImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); HashMap<TextAttribute, Object> map = new HashMap<TextAttribute, Object>(); map.put(TextAttribute.FAMILY, font.getFamily()); map.put(TextAttribute.SIZE, font.getSize()); map.put(TextAttribute.FOREGROUND, Color.BLACK); AttributedString aString = new AttributedString(text, map); AttributedCharacterIterator paragraph = aString.getIterator(); // index of the first character in the paragraph. int paragraphStart = paragraph.getBeginIndex(); // index of the first character after the end of the paragraph. int paragraphEnd = paragraph.getEndIndex(); Graphics2D graphics = textImage.createGraphics(); FontRenderContext frc = graphics.getFontRenderContext(); // The LineBreakMeasurer used to line-break the paragraph. LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, frc); // Set break width to width of Component. float breakWidth = width; float drawPosY = 0; // Set position to the index of the first character in the paragraph. lineMeasurer.setPosition(paragraphStart); // Get lines until the entire paragraph has been displayed. while (lineMeasurer.getPosition() < paragraphEnd) { // Retrieve next layout. A cleverer program would also cache // these layouts until the component is re-sized. TextLayout layout = lineMeasurer.nextLayout(breakWidth); // Compute pen x position. If the paragraph is right-to-left we // will align the TextLayouts to the right edge of the panel. // Note: this won't occur for the English text in this sample. // Note: drawPosX is always where the LEFT of the text is placed. float drawPosX = layout.isLeftToRight() ? 0 : breakWidth - layout.getAdvance(); // Move y-coordinate by the ascent of the layout. drawPosY += layout.getAscent(); // Draw the TextLayout at (drawPosX, drawPosY). layout.draw(graphics, drawPosX, drawPosY); // Move y-coordinate in preparation for next layout. drawPosY += layout.getDescent() + layout.getLeading(); } getTextLayers().add(textImage); } public void generate() throws IOException { Dimension size = getMaxSize(); BufferedImage finalImage = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_ARGB); for (BufferedImage img : getLayers()) { finalImage.createGraphics().drawImage(img, 0, 0, size.width, size.height, 0, 0, img.getWidth(null), img.getHeight(null), null); } for(BufferedImage text: getTextLayers()){ finalImage.createGraphics().drawImage(text, 0, 0, text.getWidth(), text.getHeight(), 0, 0, text.getWidth(null), text.getHeight(null), null); } File outputfile = new File("saved.png"); ImageIO.write(finalImage, "png", outputfile); } /** * @return the textLayers */ public ArrayList<BufferedImage> getTextLayers() { return textLayers; } /** * @param textLayers the textLayers to set */ public void setTextLayers(ArrayList<BufferedImage> textLayers) { this.textLayers = textLayers; } }
It still needs some refining specially on the placement of the text but it works. I guess I can implement a xml format to store all this information so is easily configurable. In the example below suns are drawn on top of rain, and the text is on top of all that. For my application each layer will build together the page I want.
Here are the images I used:
And the final result:
来源:https://stackoverflow.com/questions/7730535/how-to-make-a-dynamic-image-at-run-time