Java tutorial
import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Checkbox; import java.awt.Choice; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.Frame; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsEnvironment; import java.awt.GridLayout; import java.awt.Image; import java.awt.Label; import java.awt.MediaTracker; import java.awt.Panel; import java.awt.RenderingHints; import java.awt.Toolkit; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.font.FontRenderContext; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.net.URL; import java.text.NumberFormat; import java.util.Random; import javax.swing.JFrame; import javax.swing.JPanel; public class TextBouncer extends JPanel implements Runnable { private boolean trucking = true; private long[] previousTimes; // milliseconds private int previousIndex; private boolean previousFilled; private double frameRate; // frames per second private Image image = null; public static void main(String[] args) { String s = "Java Source and Support"; final int size = 64; if (args.length > 0) s = args[0]; Panel controls = new Panel(); final Choice choice = new Choice(); GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); Font[] allFonts = ge.getAllFonts(); for (int i = 0; i < allFonts.length; i++) choice.addItem(allFonts[i].getName()); Font defaultFont = new Font(allFonts[0].getName(), Font.PLAIN, size); final TextBouncer bouncer = new TextBouncer(s, defaultFont); Frame f = new AnimationFrame(bouncer); f.setFont(new Font("Serif", Font.PLAIN, 12)); controls.add(bouncer.createCheckbox("Antialiasing", TextBouncer.ANTIALIASING)); controls.add(bouncer.createCheckbox("Gradient", TextBouncer.GRADIENT)); controls.add(bouncer.createCheckbox("Shear", TextBouncer.SHEAR)); controls.add(bouncer.createCheckbox("Rotate", TextBouncer.ROTATE)); controls.add(bouncer.createCheckbox("Axes", TextBouncer.AXES)); Panel fontControls = new Panel(); choice.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent ie) { Font font = new Font(choice.getSelectedItem(), Font.PLAIN, size); bouncer.setFont(font); } }); fontControls.add(choice); Panel allControls = new Panel(new GridLayout(2, 1)); allControls.add(controls); allControls.add(fontControls); f.add(allControls, BorderLayout.NORTH); f.setSize(300, 300); f.setVisible(true); } private boolean mAntialiasing = false, mGradient = false; private boolean mShear = false, mRotate = false, mAxes = false; public static final int ANTIALIASING = 0; public static final int GRADIENT = 1; public static final int SHEAR = 2; public static final int ROTATE = 3; public static final int AXES = 5; private float mDeltaX, mDeltaY; private float mX, mY, mWidth, mHeight; private float mTheta; private float mShearX, mShearY, mShearDeltaX, mShearDeltaY; private String mString; public TextBouncer(String s, Font f) { previousTimes = new long[128]; previousTimes[0] = System.currentTimeMillis(); previousIndex = 1; previousFilled = false; mString = s; setFont(f); Random random = new Random(); mX = random.nextFloat() * 500; mY = random.nextFloat() * 500; mDeltaX = random.nextFloat() * 3; mDeltaY = random.nextFloat() * 3; mShearX = random.nextFloat() / 2; mShearY = random.nextFloat() / 2; mShearDeltaX = mShearDeltaY = .05f; FontRenderContext frc = new FontRenderContext(null, true, false); Rectangle2D bounds = getFont().getStringBounds(mString, frc); mWidth = (float) bounds.getWidth(); mHeight = (float) bounds.getHeight(); // Make sure points are within range. addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent ce) { Dimension d = getSize(); if (mX < 0) mX = 0; else if (mX + mWidth >= d.width) mX = d.width - mWidth - 1; if (mY < 0) mY = 0; else if (mY + mHeight >= d.height) mY = d.height - mHeight - 1; } }); } public void setSwitch(int item, boolean value) { switch (item) { case ANTIALIASING: mAntialiasing = value; break; case GRADIENT: mGradient = value; break; case SHEAR: mShear = value; break; case ROTATE: mRotate = value; break; case AXES: mAxes = value; break; default: break; } } protected Checkbox createCheckbox(String label, final int item) { Checkbox check = new Checkbox(label); check.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent ie) { setSwitch(item, (ie.getStateChange() == ie.SELECTED)); } }); return check; } public void timeStep() { Dimension d = getSize(); if (mX + mDeltaX < 0) mDeltaX = -mDeltaX; else if (mX + mWidth + mDeltaX >= d.width) mDeltaX = -mDeltaX; if (mY + mDeltaY < 0) mDeltaY = -mDeltaY; else if (mY + mHeight + mDeltaY >= d.height) mDeltaY = -mDeltaY; mX += mDeltaX; mY += mDeltaY; mTheta += Math.PI / 192; if (mTheta > (2 * Math.PI)) mTheta -= (2 * Math.PI); if (mShearX + mShearDeltaX > .5) mShearDeltaX = -mShearDeltaX; else if (mShearX + mShearDeltaX < -.5) mShearDeltaX = -mShearDeltaX; if (mShearY + mShearDeltaY > .5) mShearDeltaY = -mShearDeltaY; else if (mShearY + mShearDeltaY < -.5) mShearDeltaY = -mShearDeltaY; mShearX += mShearDeltaX; mShearY += mShearDeltaY; } public void paint(Graphics g) { Graphics2D g2 = (Graphics2D) g; setAntialiasing(g2); setTransform(g2); setPaint(g2); // Draw the string. g2.setFont(getFont()); g2.drawString(mString, mX, mY + mHeight); drawAxes(g2); } protected void setAntialiasing(Graphics2D g2) { if (mAntialiasing == false) return; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } protected void setTransform(Graphics2D g2) { Dimension d = getSize(); int cx = d.width / 2; int cy = d.height / 2; g2.translate(cx, cy); if (mShear) g2.shear(mShearX, mShearY); if (mRotate) g2.rotate(mTheta); g2.translate(-cx, -cy); } protected void setPaint(Graphics2D g2) { if (mGradient) { GradientPaint gp = new GradientPaint(0, 0, Color.blue, 50, 25, Color.green, true); g2.setPaint(gp); } else g2.setPaint(Color.orange); } protected void drawAxes(Graphics2D g2) { if (mAxes == false) return; g2.setPaint(getForeground()); g2.setStroke(new BasicStroke()); Dimension d = getSize(); int side = 20; int arrow = 4; int w = d.width / 2, h = d.height / 2; g2.drawLine(w - side, h, w + side, h); g2.drawLine(w + side - arrow, h - arrow, w + side, h); g2.drawLine(w, h - side, w, h + side); g2.drawLine(w + arrow, h + side - arrow, w, h + side); } public void run() { while (trucking) { render(); timeStep(); calculateFrameRate(); } } protected void render() { Graphics g = getGraphics(); if (g != null) { Dimension d = getSize(); if (checkImage(d)) { Graphics imageGraphics = image.getGraphics(); // Clear the image background. imageGraphics.setColor(getBackground()); imageGraphics.fillRect(0, 0, d.width, d.height); imageGraphics.setColor(getForeground()); // Draw this component offscreen. paint(imageGraphics); // Now put the offscreen image on the screen. g.drawImage(image, 0, 0, null); // Clean up. imageGraphics.dispose(); } g.dispose(); } } // Offscreen image. protected boolean checkImage(Dimension d) { if (d.width == 0 || d.height == 0) return false; if (image == null || image.getWidth(null) != d.width || image.getHeight(null) != d.height) { image = createImage(d.width, d.height); } return true; } protected void calculateFrameRate() { // Measure the frame rate long now = System.currentTimeMillis(); int numberOfFrames = previousTimes.length; double newRate; // Use the more stable method if a history is available. if (previousFilled) newRate = (double) numberOfFrames / (double) (now - previousTimes[previousIndex]) * 1000.0; else newRate = 1000.0 / (double) (now - previousTimes[numberOfFrames - 1]); firePropertyChange("frameRate", frameRate, newRate); frameRate = newRate; // Update the history. previousTimes[previousIndex] = now; previousIndex++; if (previousIndex >= numberOfFrames) { previousIndex = 0; previousFilled = true; } } public double getFrameRate() { return frameRate; } // Property change support. private transient AnimationFrame mRateListener; public void setRateListener(AnimationFrame af) { mRateListener = af; } public void firePropertyChange(String name, double oldValue, double newValue) { mRateListener.rateChanged(newValue); } private static Component sComponent = new Component() { }; private static final MediaTracker sTracker = new MediaTracker(sComponent); private static int sID = 0; public static boolean waitForImage(Image image) { int id; synchronized (sComponent) { id = sID++; } sTracker.addImage(image, id); try { sTracker.waitForID(id); } catch (InterruptedException ie) { return false; } if (sTracker.isErrorID(id)) return false; return true; } public Image blockingLoad(String path) { Image image = Toolkit.getDefaultToolkit().getImage(path); if (waitForImage(image) == false) return null; return image; } public static Image blockingLoad(URL url) { Image image = Toolkit.getDefaultToolkit().getImage(url); if (waitForImage(image) == false) return null; return image; } public BufferedImage makeBufferedImage(Image image) { return makeBufferedImage(image, BufferedImage.TYPE_INT_RGB); } public BufferedImage makeBufferedImage(Image image, int imageType) { if (waitForImage(image) == false) return null; BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), imageType); Graphics2D g2 = bufferedImage.createGraphics(); g2.drawImage(image, null, null); return bufferedImage; } } class AnimationFrame extends JFrame { private Label mStatusLabel; private NumberFormat mFormat; public AnimationFrame(TextBouncer ac) { super(); setLayout(new BorderLayout()); add(ac, BorderLayout.CENTER); add(mStatusLabel = new Label(), BorderLayout.SOUTH); // Create a number formatter. mFormat = NumberFormat.getInstance(); mFormat.setMaximumFractionDigits(1); // Listen for the frame rate changes. ac.setRateListener(this); // Kick off the animation. Thread t = new Thread(ac); t.start(); } public void rateChanged(double frameRate) { mStatusLabel.setText(mFormat.format(frameRate) + " fps"); } }