CoderMusgrove
CoderMusgrove

Reputation: 614

Java: Graphics aligning Strings

I'm currently trying to create a rolling text method, in which will take an argument of a String. Soon, it will start to draw, from left to right, and go onto new lines over time so it doesn't draw off of the screen. I am using the FontMetrics to try to achieve my goal.

I pass arguments to my RollingText.render(Graphics g, int x, int y) from my render method in my main class. From within my render method, I start settings the Graphics font, and color, and grabbing all of the FontMetrics, and I start grabbing different stuff from the FontMetrics. As well, I go into a for loop for drawing the text to the string.

public void render(Graphics g, int x, int y) {
    // text is the field that a grab the index of the string from
    // the index is the max part of the string I'm grabbing from,
    // increments using the update() method
    String str = text.substring(0, index);
    String[] words = str.split(" ");

    Font f = new Font(g.getFont().getName(), 0, 24);
    FontMetrics fmetrics = g.getFontMetrics(f);
    g.setColor(Color.white);
    g.setFont(f);

    int line = 1;
    int charsDrawn = 0;
    int wordsDrawn = 0;
    int charWidth = fmetrics.charWidth('a');
    int fontHeight = fmetrics.getHeight();
    for (int i = 0; i < words.length; i++) {
        int wordWidth = fmetrics.stringWidth(words[i]);
        if (wordWidth* wordsDrawn + charWidth * charsDrawn > game.getWidth()) {
            line++;
            charsDrawn = 0;
            wordsDrawn = 0;
        }

        g.drawString(words[i], x * charsDrawn + charWidth, y + fontHeight * line);

        charsDrawn += words[i].length();
        wordsDrawn += 1;
    }
}

Currently, at this point everything will work fine, but the problem left is that the spaces between each word in the drawString method is heavily exaggerated, like so:

Rolling Text Demo

The line:

g.drawString(words[i], x * charsDrawn + charWidth, y + fontHeight * line);

The only problem I'm currently having is finding the correct way to do the math for the x position. Currently, it is spaced heavily and dynamically depending on word length, and I cannot figure out how to get it to look at least normal. I've tried different combinations with my currently defined integers, and so forth. Issues that occur will be anywhere from incorrect spacing, to immoderate positioning and flickering, as well as text running together.

In the end, my question is, what kind of algorithm would be used to help me correctly position the x coordinates for the text to look correct?

Upvotes: 0

Views: 290

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347184

I'm not sure why you're trying calculate the x/y position based on a variety of different variables, when simply adding the FontMetrics#stringWidth or FontMetrics#getHeight to the x/y values would be simpler, in my mind...

You also seem to be switching between using FontMetrics#stringWidth and caclulating the width of individual characters based on "sample" width...wordsDrawn + charWidth, this is making your calculations simply...confusing. You also seem to have a precedence issue...

Shouldn't x * charsDrawn + charWidth be x + (charsDrawn * charWidth)?

I've not actually been able to get your example code to reproduce your results, instead, I simplified it...

TextLayout

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class RenderText {

    public static void main(String[] args) {
        new RenderText();
    }

    public RenderText() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private String text;
        private int index;

        public TestPane() {
            text = "A long time ago, in a galaxy far, far, away..."
                    + "A vast sea of stars serves as the backdrop for the main title. "
                    + "War drums echo through the heavens as a rollup slowly crawls "
                    + "into infinity."
                    + "     It is a period of civil war. Rebel spaceships, "
                    + "     striking from a hidden base, have won their first "
                    + "     victory against the evil Galactic Empire."
                    + "     During the battle, Rebel spies managed to steal "
                    + "     secret plans to the Empire's ultimate weapon, the "
                    + "     Death Star, an armored space station with enough "
                    + "     power to destroy an entire planet."
                    + "     Pursued by the Empire's sinister agents, Princess "
                    + "     Leia races home aboard her starship, custodian of "
                    + "     the stolen plans that can save her people and "
                    + "     restore freedom to the galaxy...";

            index = text.length() - 1;
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            render(g, 0, 0);
            g2d.dispose();
        }

        public void render(Graphics g, int x, int y) {
            // text is the field that a grab the index of the string from
            // the index is the max part of the string I'm grabbing from,
            // increments using the update() method
            String str = text.substring(0, index);
            String[] words = str.split(" ");

            Font f = new Font(g.getFont().getName(), 0, 24);
            FontMetrics fmetrics = g.getFontMetrics(f);
//            g.setColor(Color.white);
            g.setFont(f);

            int fontHeight = fmetrics.getHeight();

            int spaceWidth = fmetrics.stringWidth(" ");

            int xPos = x;
            int yPos = y;
            for (String word : words) {
                int wordWidth = fmetrics.stringWidth(word);
                if (wordWidth + xPos > getWidth()) {
                    yPos += fontHeight;
                    xPos = x;
                }
                g.drawString(word, x + xPos, yPos + fmetrics.getAscent());
                xPos += wordWidth + spaceWidth;
            }
        }
    }

}

Until you can provide an actual runnable example that demonstrates your problem, this is the best I can do...

Upvotes: 1

Related Questions