【问题标题】:How do i align this text correctly?如何正确对齐此文本?
【发布时间】:2011-09-08 10:39:57
【问题描述】:

我今天写了这个极地时钟,我几乎完成了,除了我想在类似于this 的行内对齐我的文本。有谁知道如何做到这一点?我尝试使用 FontRenderContext 和字体指标,但我似乎无法让它工作。这是完整的源代码,您可以自己编译并查看。

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]);
    }
}

}

【问题讨论】:

    标签: java swing applet rotation java-2d


    【解决方案1】:

    这是一个旋转文本的简单示例。

    附录:您需要将文本的径向起点调整为stringWidth(name[n])。您的程序似乎正在旋转单个字符以努力跟随圆弧,而该示例似乎是在与圆弧相切的直线上绘制文本。后一种方法可能证明更简单。例如,此变体将标签集中在弧的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);
    }
    

    为方便起见,上面的代码来回执行rotate();为了比较,这是显示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);
                }
            });
        }
    }
    

    【讨论】:

    • 我已经有一个这样的版本,以前可以使用海峡字符串,但感谢您的帮助。我不知道您可以像在示例中那样使用 setTransform 方法。它真的很有帮助。通过使用默认的 AffineTransform 而不是向后旋转,它提供了大约 +30 fps;
    • 我认为在这种情况下旋转文本是不够的。您必须能够沿着任意曲线绘制文本。请参阅下面的答案。
    • @stas:太好了!我认为关闭getStartPoint() 也可以节省一些计算量。如果需要,您可以返回并应用@eugener 的有用建议之一。
    【解决方案2】:

    您必须能够沿曲线绘制文本。有几种方法可以做到这一点,但最简单的一种是使用Stroke API。你可以在http://www.jhlabs.com/java/java2d/strokes/找到一个例子

    另一种方法是使用仿射变换。例子在http://www.java2s.com/Code/Java/2D-Graphics-GUI/Drawtextalongacurve.htm

    【讨论】:

    • +1 我明白你的意思;我之前没见过Text Along a Path
    • 谢谢,但我已经尝试了这两种方法,但我似乎无法让它们工作。也许我只是做错了什么?
    • 我承认,我不喜欢 RollingText 的变体。字形从来没有相当与基线正常。
    猜你喜欢
    • 1970-01-01
    • 2011-09-16
    • 1970-01-01
    • 2014-08-08
    • 2020-06-30
    • 1970-01-01
    • 2016-09-21
    • 2013-02-11
    • 2013-03-18
    相关资源
    最近更新 更多