将字符串的 ArrayList 与(自定义)Box 对齐不会在正确的位置

Aligning an ArrayList of Strings into a (custom)Box won't get in proper position

我在将一组字符串与 Java 中的框底部对齐时遇到了一点问题。

NOTE: the Box class I'm using is not a default > javax.Swing box! It's a simple costum box with a x, y position, and with a width and height!

我目前拥有什么?

我的代码应该如何工作:

(页面下方的代码片段)

Message 对象可以使用不同的 Font 设置。 Allign class 可以对齐这些 Messages 对象。 Message class 包含一个名为 obtainFontDimension() 的方法,它在首选 Font 设置中获取对象字符串的边界。当我们想要对 Message 对象应用对齐时,我创建了一个包含 x、y 位置以及宽度和高度的 Box 对象。通过调用 Allign.applyAllignment(params),将应用计算来对齐 Box 中的 Message 对象,并且 Message 的 dx、dy(绘制 x、y 位置)将是设置。

到这里为止一切正常。

现在我创建一个 MessageList 对象并向其添加一些 (Message 对象。对此应用对齐时,它将 运行 通过 Message 对象它包含,并将在它们上调用 obtainFontDimensions()。将对这些字符串的高度求和,并得出字符串的总高度(int listHeight)。获取每个 [ 的绘制位置=12=] 对象,我们在我们想要对齐的 Box 的 y 位置。首先我们删除 Box 的 y 位置的 listHeight

现在我们得到了第一个字符串的偏移量。当应用底部对齐时,它只是将 Box 的高度添加到偏移量中。最后,通过将当前 Message 对象高度添加到偏移量来为下一个 Message 对象设置偏移量。下一次迭代的时间,直到 ArrayList 已完全计算。这应该导致以下结果:

出了什么问题?

当对 MessageList 对象应用对齐时,一些字符串完美地相互接触(参见图像上的圆圈 B),而一些字符串与其他像素保持更远的距离(参见图像上的圆圈 A1、A2)。紧接着,底部还有一个意想不到的填充(见图片上的圆圈 C)。

到目前为止我尝试了什么?

代码片段

(仅重要部分)

public class Window()
{
    public void draw(Graphics2D g2d)
    {
        Box contentBox = new Box(100, 100, 300, 300);
        Message loadTitle = new Message("This is a testing TITLE", Colors.ORANGE, Fonts.LOADING_TITLE, false);
        Message loadDescription = new Message(loadString, Colors.RED, Fonts.LOADING_DESCRIPTION, false);
        Message loadTip = new Message("This is a random TIP!", Colors.RED, Fonts.LOADING_DESCRIPTION, false);
        Message loadRelease = new Message("Planned game release 2939!", Colors.RED, Fonts.LOADING_DESCRIPTION, false);
        Message loadSingle = new Message("THIS IS A SINGLE MESSAGE! 2o15", Colors.RED, Fonts.LOADING_DESCRIPTION, false);

        MessageList list = new MessageList();
        list.add(loadTitle);
        list.add(loadDescription);
        list.add(loadTip);
        list.add(loadRelease);
        list.add(loadSingle);
        Allign.applyAllignment(g2d, Allignment.BOTTOM_RIGHT, list, loadBox);

        loadBox.testDraw(g2d);
        loadTitle.draw(g2d);
        loadDescription.draw(g2d);
        loadTip.draw(g2d);
        loadRelease.draw(g2d);
        loadSingle.draw(g2d);
    }
}

public class Message 
{   
    private String text;
    private Color color;
    private Font font;
    private int dx, dy;
    private int width, height;
    private Rectancle2D vb; // temp

    public Message(String text, int x, int y, Color color, Font font)
    {
        // set text, color, font, x, y..
    }
    public Rectangle2D obtainFontDimension(Graphics2D g2d)
    {
       if(font == null){ font = Fonts.DEFAULT; }
        g2d.setFont(font);
    
        FontRenderContext frc = g2d.getFontRenderContext();
       GlyphVector gv = g2d.getFont().createGlyphVector(frc, text);
        Rectangle2D vb = gv.getPixelBounds(null, 0, 0);

        this.width = (int)vb.getWidth();
        this.height = (int)vb.getHeight();
        this.gv = gv; // TEMP for bound drawing
    
        return vb;
    }
    public void draw(Graphics2D g2d)
    {
        g2d.setFont(font);
        g2d.setColor(color);
        g2d.drawString(text, dx, dy);
    
        // TEMP draw bounds
        g2d.setColor(new Color(0, 0, 0, 100));
        g2d.draw(gv.getPixelBounds(null, dx, dy));
    }
}

public class Allign
{
    public static enum Allignment
    {
        BOTTOM_RIGHT
        //, etc
    }
    public static void applyAllignment(Graphics2D g2d, Allignment allignment, Object object, Box box)
    {
        Point position = null;
        Point dimension = null;

        if(obj instanceof Message){ // Single Message object }
        else if(obj instanceof Message)
        {
         MessageList messageList = (MessageList) obj;
        
            int listHeight = 0;
            for(Message message : messageList.getList())
            {
                listHeight += message.obtainFontDimension(g2d).getHeight();
            }
            position = new Point(box.x, box.y-listHeight); // offset Y
        
           for(Message message : messageList.getList())
            {
                message.setDrawPosition(allign(allignment, position, new Dimension(message.getWidth(), 0), box, true));
                position.y += message.getHeight();
            }
        }
    }
    private static Point allign(Allignment allignment, Point position, Dimension dimension, Box box, boolean verticalAllign)
    {
        switch(allignment)
        {
            case BOTTOM_RIGHT:
                position = allignRight(position, dimension, box);
                if(!verticalAllign) break;
                position = allignBottom(position, dimension, box);
                break;
            // Rest
        }
    }
    private static Point allignBottom(Point position, Dimension dimension, Box box)
    {
        return new Point(position.x, position.y+box.height-dimension.height);
    }
}

我可以推荐这样的实用程序 class 吗?

public class TextPrinter {

    public enum VerticalAlign {

        TOP,
        MIDDLE,
        BOTTOM
    }

    public enum HorizontalAlign {

        LEFT,
        CENTER,
        RIGHT
    }

    private Font font;
    private Color color;
    private int width;
    private int height;
    private VerticalAlign vAlign = VerticalAlign.TOP;
    private HorizontalAlign hAlign = HorizontalAlign.LEFT;

    public Font getFont() {
        return font;
    }

    public void setFont(Font font) {
        this.font = font;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public VerticalAlign getVerticalAlign() {
        return vAlign;
    }

    public void setVerticalAlign(VerticalAlign vAlign) {
        this.vAlign = vAlign;
    }

    public HorizontalAlign getHorizontalAlign() {
        return hAlign;
    }

    public void setHorizontalAlign(HorizontalAlign hAlign) {
        this.hAlign = hAlign;
    }

    public Color getColor() {
        return color;
    }

    public void setColor(Color color) {
        this.color = color;
    }

    private int getOffSetX(int widthText){
        int result = 0;
        if (hAlign == HorizontalAlign.CENTER){
            result = (width - widthText)/2;
        } else if (hAlign == HorizontalAlign.RIGHT){
            result = width - widthText;
        }
        return result;
    }

    private int getOffSetY(int ascent, int descent){
        int result = ascent;
        if (vAlign == VerticalAlign.MIDDLE){
            result = (height + ascent - descent)/2;
        } else if (vAlign == VerticalAlign.BOTTOM){
            result = height - descent;
        }
        return result;
    }

    public void print(Graphics g, String text, int x, int y) {
        g.setColor(color);
        g.setFont(font);
        FontMetrics fm = g.getFontMetrics(font);

        int widthText = fm.stringWidth(text);
        g.drawString(text, x + getOffSetX(widthText), y + getOffSetY(fm.getAscent(), fm.getDescent()));
    }
}

我在我的扑克游戏中使用它: https://github.com/dperezcabrera/jpoker

感谢 David Perez 和 VGR 的投入!

我已经切换回字体指标,抓取字符串边界的高度。但是,这仅使用基线到最高上升点的高度。为了获得适当的底部间距(如顶部),我还添加了下降整数。

public int obtainFontDimension(Graphics2D g2d)
{
    if(font == null){ font = Fonts.DEFAULT; }
    g2d.setFont(font);

    FontMetrics fm = g2d.getFontMetrics(font);
    Rectangle2D sb = fm.getStringBounds(text, g2d);
    this.width = (int)sb.getWidth();
    this.height = (int)sb.getHeight();
    this.descent = (int)fm.getDescent(); // added
    tempShape = new Rectangle(width, height+descent); // Temp for drawing bounds

    return height;
}

在开始对齐之前,我先计算MessageList中所有String的总高度。总高度是字符串的高度包括下降高度。

然后我需要为我添加的每个垂直对齐可能性获取偏移高度。 (顶部、中间、底部)

之后,我们通过在每次迭代时将高度和下降添加到偏移量来对齐彼此下方的每个消息,这次禁用垂直对齐,否则它将水平重新对齐每个消息作为单个消息对象而不是作为一个组的消息,但它确实允许它垂直对齐。

if (obj instanceof MessageList)
{
    MessageList messageList = (MessageList) obj;

    int listHeight = 0;
    for(Message message : messageList.getList())
    {
        message.obtainFontDimension(g2d);
        listHeight += message.getHeight()+message.getDescent();
    }

    position = new Point(box.x, box.y);
    Dimension listDimension = new Dimension(0, listHeight);
    if( allignment == Allignment.MIDDLE || allignment == Allignment.MIDDLE_LEFT
    || allignment == Allignment.ABSOLUTE_MIDDLE || allignment == Allignment.MIDDLE_RIGHT)
    {
        position = allign(Allignment.MIDDLE, position, listDimension, box, true);
    }
    else if(allignment == Allignment.BOTTOM || allignment == Allignment.BOTTOM_LEFT
    ||       allignment == Allignment.BOTTOM_CENTER ||  allignment == Allignment.BOTTOM_RIGHT)
    {
        position = allign(Allignment.BOTTOM, position, listDimension, box, true); 
    }
    else
    {
        position = allign(Allignment.TOP, position, listDimension, box, true); 
    }

    for(Message message : messageList.getList())
    {
        position.y += message.getHeight(); // prepare the offset
        message.setDrawPosition(allign(allignment, position, new Dimension(message.getWidth(), 0), box, true));
        position.y += message.getDescent(); // add descending offset for next itteration
    }
}

使用新边界绘制消息:

public void draw(Graphics2D g2d)
{
    g2d.setFont(font);
    g2d.setColor(color);
    g2d.drawString(text, dx, dy);

    // Drawing bounds for testing
    g2d.setColor(new Color(0, 0, 0, 100));
    shape.setLocation(dx, dy-height);
    g2d.draw(tempShape);
}

最终结果:

再次感谢!