bracco23 bracco23 - 1 month ago 19
Java Question

MigLayout and JLabel derivative show strange behaviour and don't follow layout

I'm using Java Swing and MigLayout (What a wonderful tool!) to create a project in java, and I got a problem.

To display every string as big as I could, I created a sub-class of JLabel, that changes the font according to the size of the component, I'll attach the code in the example I'll provide.

The project is really big and there are a lot of panels nested, I also change the content of the main window on the fly, validating everything after.

But, if I try to use the cell disposition of components within MigLayout, evrything is wrong.

If i use the same layout, with the same constraint, but instead of using my custom label, i use an ordinary JLabel, everything works like a charm.

Here the gist of the example:

https://gist.github.com/bracco23/c47975ede0d857ac3b134f197c4371a2

The code is in two files:


  • JAdaptiveLabel.java, the custom component that just recalculate the optimal font size whenever text is changed or on demand.

  • test.java, a mock example. Changing CUSTOM you can switch between my component and a plain JLabel and see the difference. The intended layout is the one with the plain JLabel.



Can anybody give me a clue of what's wrong and how could I fix it?

Answer

Ok, after trying hard I solved.

After the test, I came to the conclusion (obvious) that somethig was wrong with my JAdaptiveLabel. So I searched online for another version, to see if it was my implementation or the adaptivity itself the problem.

I came to this answer: @Warren K

I used his class as it was and it worked, so my implementation was bugged.

I started from his version and changed the resizing algorithm, since his was iterative (change size till you find the perfect one) and mine was mathematical (just get the involved measures and calculate the perfect size).

It worked. Now the layout get disposed properly and the label changes font size if I resize the window.

Here the code modified:

package it.bracco23.util;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;

// Improved version of http://java-sl.com/tip_adapt_label_font_size.html

public class JAdaptiveLabel extends JLabel {

    private Graphics g;
    private boolean init = false;

    public JAdaptiveLabel(String text, Icon icon, int horizontalAlignment) {
        super(text, icon, horizontalAlignment);
        init();
    }

    public JAdaptiveLabel(String text, int horizontalAlignment) {
        super(text, horizontalAlignment);
        init();
    }

    public JAdaptiveLabel(String text) {
        super(text);
        init();
    }

    public JAdaptiveLabel(Icon image, int horizontalAlignment) {
        super(image, horizontalAlignment);
        init();
    }

    public JAdaptiveLabel(Icon image) {
        super(image);
        init();
    }

    public JAdaptiveLabel() {
        init();
    }



    protected void init() {
        addComponentListener(new ComponentAdapter() {
            public void componentResized(ComponentEvent e) {
                adaptLabelFont(JAdaptiveLabel.this);
            }
        });
        init = true;
    }

   protected void adaptLabelFont(JLabel l) {
        if (g==null) {
            return;
        }

        Rectangle r  = l.getBounds();
        Insets ins = l.getInsets();
        r.x          = 0;    
        r.y          = 0;    
        Font f       = l.getFont();
        Dimension dim = getTextSize(l, f);
        //0.9f is a scale factor to don't let the text take too much space
        //without it will work, but the text may appear to close to the border
        float xFactor = ((r.width - ins.left - ins.right) * 0.9f) / dim.width;
        float yFactor = ((r.height - ins.top - ins.bottom) * 0.9f) / dim.height;

        /*the next lines assure the scaling factors are not zero (can happen)
        and are different enough from 1. Without this last check, it might happen
        that the font starts to cycle between two sizes. */
        xFactor = (xFactor != 0 && Math.abs(xFactor - 1)>0.1) ? xFactor : 1;
        yFactor = (yFactor != 0 && Math.abs(xFactor - 1)>0.1) ? yFactor : 1;
        float fontSize = f.getSize() * Math.min(xFactor, yFactor);

        setFont(f.deriveFont(f.getStyle(), fontSize));
        repaint();
    }

    private Dimension getTextSize(JLabel l, Font f) {
        Dimension size  = new Dimension();
        FontMetrics fm  = g.getFontMetrics(f);
        size.width      = fm.stringWidth(l.getText());
        size.height     = fm.getHeight();
        return size;
    }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        this.g=g;
    }

      @Override
    public void setText(String text) {
        super.setText(text);
        if(init){
           adaptLabelFont(this);
        }
    }

}

If you use this version of the class inside the example I gave, everything works fine!

P.S. I also added a call to the resizing method in setText, since you must change the size when the label resize or change its content.

Comments