How do i align this text correctly?

2019-01-06 23:23发布

问题:

I wrote this polar clock today and i am almost finished exept i want to align my text inside the line similar to this. Does anyone know how to do this? Ive tried to use FontRenderContext and font metrics but i cant seem to get it to work. Here is the whole source code so you can compile it and see for yourselves.

import java.applet.Applet;
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Toolkit;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.util.Calendar;
import java.util.TimeZone;

public class Clock extends Applet implements Runnable {

int[][] colorsInt = {{20,20,20},{100,100,50},{50,100,100},{10,170,50},{79,29,245},{24,69,234},{253,24,103}};
Color[] colors;
int size;
int radius;
boolean anitalias = false;
static final float HPI = (float)(Math.PI / 180f);

public void start() {
    enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
    new Thread(this).start();
}

public void run() {
    setSize(500, 500); // For AppletViewer, remove later.

    // Set up the graphics stuff, double-buffering.
    BufferedImage screen = new BufferedImage(800, 600, BufferedImage.TYPE_INT_RGB);
    Graphics2D g = (Graphics2D)screen.getGraphics();

    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    WritableRaster wr = screen.getRaster();
    Graphics appletGraphics = getGraphics();

    // Some variables to use for the fps.
    long fpstn = 1000000000 / 600;
    int tick = 0, fps = 0, acc = 0;
    long lastTime = System.nanoTime();

    // Vars
    Calendar c;
    size = 500;
    radius = size / 2;
    Arc2D.Float arch;
    float scale, radians;
    long miliSecond;
    int second, minute, hour, month, year, dayOfWeek, dayOfMonth, dayOfYear, daysInMonth, daysInYear;
    float[] tvars = new float[6];
    float[] vars = new float[6];
    String[] names = new String[6];
    FontMetrics fm = g.getFontMetrics();
    Font font = g.getFont();
    FontRenderContext frc = g.getFontRenderContext();
    GlyphVector gv = font.createGlyphVector(frc, "Hello world");
    int length = gv.getNumGlyphs();

    // Init
    initColors();
    for (int i = 0; i < vars.length; i++)
        vars[i] = 0;

    // Game loop.
    while (true) {
        long now = System.nanoTime();
        acc += now - lastTime;
        tick++;
        if (acc >= 1000000000L) {
            acc -= 1000000000L;
            fps = tick;
            tick = 0;
        }

        // Update
        c = Calendar.getInstance();
        miliSecond = c.get(Calendar.MILLISECOND);
        second = c.get(Calendar.SECOND);
        minute = c.get(Calendar.MINUTE);
        hour = c.get(Calendar.HOUR_OF_DAY);
        dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
        dayOfYear = c.get(Calendar.DAY_OF_YEAR);
        dayOfWeek = c.get(Calendar.DAY_OF_WEEK);
        month = c.get(Calendar.MONTH);
        daysInMonth = c.getActualMaximum(Calendar.DAY_OF_MONTH);
        daysInYear = c.getActualMaximum(Calendar.DAY_OF_YEAR);

        tvars[0] = (second * 1000 + miliSecond) / 60000f * 360f;
        tvars[1] = (minute * 60f + second) / 3600f * 360f;
        tvars[2] = (hour * 60f + minute) / 1440f * 360f;
        tvars[3] = ((dayOfWeek - 2) * 24f + hour) / 168f * 360f;
        tvars[4] = ((dayOfMonth - 1) * 24f + hour) / (daysInMonth * 24f) * 360f;
        tvars[5] = dayOfYear / (float)daysInYear * 360f;

        for (int i = 0; i < vars.length; i++) {
            if (tvars[i] - vars[i] > 1) {
                vars[i] += (tvars[i] - vars[i]) / 15;
            } else if(tvars[i] - vars[i] < -1) {
                vars[i] -= (vars[i] - tvars[i]) / 15;
            } else {
                vars[i] = tvars[i];
            }
        }

        names[0] = second + " Second" + (second > 1 ? "s" : "");

        lastTime = now;

        // Render
        g.setColor(colors[0]);
        g.fillRect(0, 0, size, size);
        for (int i = 0; i < vars.length; i++) {

            scale = i / (float)vars.length * radius * 1.7f;
            g.setColor(colors[0]);
            g.fillOval((int)(scale / 2), (int)(scale / 2), (int)(size - scale), (int)(size - scale));
            g.setColor(colors[i + 1]);
            scale += 15;
            arch = new Arc2D.Float(scale / 2, scale / 2, size - scale, size - scale, 450 - vars[i], vars[i], Arc2D.PIE);
            g.fill(arch);

            g.setColor(Color.WHITE);
            radians = (vars[i]) * HPI;// vars[i] - 90
            scale = ((float)(vars.length - i) / (float)vars.length * (float)radius / 2f * 1.7f) + 15f;

            g.translate(radius, radius);
            System.out.println(i + ": " + ((1 - scale / radius) * 2));
            for (int j = 0; j < names[0].length(); j++) {

                char ch = names[0].charAt(j);
                radians = ((vars[i] - (names[0].length() - j) * 2) * (1 + (1 - scale / radius) * 2)) * HPI;
                g.rotate(radians);
                g.drawString(ch + "", 0, -scale);
                g.rotate(-radians);
            }
            g.translate(-radius, -radius);

            /*float x = (float)Math.cos(radians) * scale;
            float y = (float)Math.sin(radians) * (vars.length - i) / vars.length * radius / 2 * 1.7f;
            g.drawRect((int)x + size / 2, (int)y + size / 2, 10, 10);*/

        }
        scale = vars.length / (float)vars.length * radius * 1.7f;
        g.setColor(colors[0]);
        g.fillOval((int)(scale / 2), (int)(scale / 2), (int)(size - scale), (int)(size - scale));

        g.setColor(Color.WHITE);
        g.drawString("FPS " + String.valueOf(fps), 20, 30);

        // Draw the entire results on the screen.
        appletGraphics.drawImage(screen, 0, 0, null);

        do {
            Thread.yield();
        } while (System.nanoTime() - lastTime < 0);
        if (!isActive()) {
            return;
        }
    }
}

public void initColors() {
    colors = new Color[colorsInt.length];
    for (int i = 0; i < colors.length; i++) {
        colors[i] = new Color(colorsInt[i][0], colorsInt[i][1], colorsInt[i][2]);
    }
}

}

回答1:

Here's a simple example of rotating text.

Addendum: You'll want to adjust the the text's radial starting point by stringWidth(name[n]). Your program appears to be rotating individual characters in a effort to follow the arc, while the example appears to be drawing the text in a straight line tangent to the arc. The latter approach may prove simpler. For example, this variation centers the labels across the arc's getStartPoint():

for (int i = 0; i < vars.length; i++) {
    ...
    String s = names[0];
    int w = fm.stringWidth(s);
    int h = fm.getHeight() + fm.getMaxDescent();
    Point2D p = arch.getStartPoint();
    int x = (int) p.getX();
    int y = (int) p.getY();
    radians = (vars[i]) * HPI;
    g.rotate(radians, x, y);
    g.drawString(s, x - w / 2, y + h);
    g.rotate(-radians, x, y);
}

For convenience the code above does rotate() to and fro; for comparison, here's the original example showing repeated concatenations of rotate():

import java.awt.*;
import java.awt.geom.AffineTransform;
import javax.swing.*;

/** @see http://stackoverflow.com/questions/6238037 */
public class RotateText extends JPanel {

    private static final Font f = new Font("Serif", Font.BOLD, 32);
    private static final String s = "Hello World!";
    private static final Color[] colors = {
        Color.red, Color.green, Color.blue, Color.cyan
    };
    private Graphics2D g2d;
    private AffineTransform at;

    public RotateText() {
        setPreferredSize(new Dimension(400, 400));
    }

    @Override
    public void paintComponent(Graphics g) {
        g2d = (Graphics2D) g;
        g2d.setFont(f);
        g2d.setColor(Color.black);
        g2d.fillRect(0, 0, getWidth(), getHeight());
        at = g2d.getTransform();
        int w = this.getWidth();
        int h = this.getHeight();
        int w2 = g2d.getFontMetrics().stringWidth(s) / 2;
        int h2 = 2 * g2d.getFontMetrics().getHeight() / 3;
        render(0, w / 2 - w2, h - h2);
        render(1, h2, h / 2 - w2);
        render(2, w / 2 + w2, h2);
        render(3, w - h2, h / 2 + w2);
        g2d.setTransform(at);
        g2d.setColor(Color.yellow);
        g2d.fillRect(w / 3, h / 3, w / 3, h / 3);
    }

    private void render(int n, int x, int y) {
        g2d.setColor(colors[n]);
        g2d.setTransform(at);
        g2d.rotate(n * Math.PI / 2, x, y);
        g2d.drawString(s, x, y);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            //@Override
            public void run() {
                JFrame f = new JFrame();
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.add(new RotateText(), BorderLayout.CENTER);
                f.pack();
                f.setVisible(true);
            }
        });
    }
}


回答2:

You have to be able to draw text along the curves. There are several ways to do it, but the simplest one is to use Stroke API. You can find an example at http://www.jhlabs.com/java/java2d/strokes/

The other way is using affine transforms. The example is at http://www.java2s.com/Code/Java/2D-Graphics-GUI/Drawtextalongacurve.htm