Texte zeichenweise übereinander darstellen, gemeinsam umbrechen

David R.

Mitglied
Hallo 🙂

Dies ist mein erster Beitrag in diesem Forum, auch wenn es mir schon früher des öfteren weitergeholfen hat. Vielen Dank dafür! (Auch für die Hilfe im IRC!)

Nun bin ich bei einem Punkt angelangt, an dem meine Frage für den IRC wahrscheinlich zu umfangreich ist. Über Ideen dazu würde ich mich sehr freuen!

Folgende Situation:

Es sollen in einem Panel zwei Texte parallel dargestellt werden. Jedem Buchstaben des einen Textes ist hierbei eindeutig ein Buchstabe des zweiten Textes zugeordnet, der genau darüber bzw. darunter steht. Wenn die Breite des Panels nicht mehr ausreicht, sollen die Texte gemeinsam umbrechen. Wenn auch die Höhe nicht ausreicht soll vertikales Scrollen möglich sein.

Bisher wurde das Problem gelöst, indem jedem Buchstabenpaar ein eigenes Panel mit zwei übereinander angeordneten Labels spendiert wurde. Diese wurden dann in ein Panel eingefügt, das das WrapLayout verwendet.

Java:
public JScrollPane createAnalysisPane(char[] plain, char[] cypher) {
    JPanel analysisPanel = new JPanel(new WrapLayout(WrapLayout.LEFT));
    for (int i = 0; i < plain.length; i++) {
        JLabel plainLabel = new JLabel(plain[i] + "");
        JLabel cypherLabel = new JLabel(cypher[i] + "");
        JPanel charPanel = new JPanel(new GridLayout(2, 1));
        charPanel.add(plainLabel);
        charPanel.add(cypherLabel);
        analysisPanel.add(charPanel);
    }
    JScrollPane analysisPane = new JScrollPane(analysisPanel);
    return analysisPane;
}

Das funktioniert soweit auch ganz gut, nur dass diese Lösung natürlich extrem inperformant ist, bei längeren Texten völlig versagt und sich auch nicht wirklich sauber anfühlt (dafür sind JLabels einfach nicht da...).

Hat wer ne Idee?
 
Nur eine kurze Zwischenfrage: Spricht was dagegen, eine ganz normale JTextArea mit
[c]Monospaced[/c]
[c]Npoptqbdfe[/c]
Font zu verwenden?
 
Naja, die Texte sollen ja gemeinsam umbrechen.

Im Moment sieht das so aus:

lxpfic5w.jpg


Bei deiner Lösung müsste man die Anzahl der Zeichen pro Zeile bei jedem Resize neu ermitteln, um anschließend einen angepassten Text zu setzen.

(Dass die zwei Texte im Beispiel unterschiedlich gefärbt sind ist zwar nett, muss aber nicht unbedingt sein.)
 
Vielleicht gibt es sowas auch schon vorgefertigt. Wobei. Eher nicht, da es recht speziell ist. Hab' mal rumgespielt. Das "highlighting" kann man ausschalten, indem man 'null' übergibt.

Java:
// Marco13 for [url=http://www.java-forum.org/awt-swing-swt/]AWT, Swing & SWT - java-forum.org[/url]
//   110869-texte-zeichenweise-uebereinander-darstellen-gemeinsam-umbrechen.html

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Scrollable;
import javax.swing.SwingUtilities;



public class TextComparePanel extends JPanel implements Scrollable
{

    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                JFrame f = new JFrame();
                String s0 = 
                    "This is a small test case "+
                    "with some wierd line wrapping "+
                    "just to test this thing. And now, "+
                    "ladies and gentlemen ... just "+
                    "kidding ;-) ... And now, gentlemen, "+
                    "the famous words: LOREM IPSUM :-)";

                String s1 = 
                    "This is a  big  test case "+
                    "with some wired line warping  "+
                    "just to test this thing. And now, "+
                    "ladies and gentlemen ... just "+
                    "kidding ;-) ... and now, gentlemen, "+
                    "the famous word : LOREM IPSUM :-)";

                TextComparePanel p = new TextComparePanel(
                    s0.toCharArray(), Color.BLACK,
                    s1.toCharArray(), Color.LIGHT_GRAY, 
                    Color.RED);
                
                f.getContentPane().add(new JScrollPane(p));
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.setSize(200,200);
                f.setVisible(true);
            }
        });
    }
    
    private char array0[];
    private Color color0;
    private char array1[];
    private Color color1;
    private Color highlight;
    private int additionalSpacing = 1;
    
    public TextComparePanel(char[] array0, Color color0, char[] array1, Color color1, Color highlight)
    {
        this.array0 = array0;
        this.color0 = color0;
        this.array1 = array1;
        this.color1 = color1;
        this.highlight = highlight;
    }
    
    public void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D)gr;
        
        FontMetrics fm = g.getFontMetrics();
        int lineHeight = fm.getHeight();
        
        int y = lineHeight;
        int x = 0;
        for (int i=0; i<array0.length; i++)
        {
            char c0 = array0[i];
            char c1 = array1[i];
            int offset0 = 0;
            int offset1 = 0;
            int w0 = fm.charWidth(c0);
            int w1 = fm.charWidth(c1);
            int maxW = -Integer.MAX_VALUE;
            if (w0 > w1)
            {
                maxW = w0;
                offset1 = (w0 - w1)/2;
            }
            else 
            {
                maxW = w1;
                offset0 = (w1 - w0)/2;
            }
            maxW += additionalSpacing;
            int nextX = x + maxW;
            if (nextX >= getWidth())
            {
                x = 0;
                y += lineHeight * 2;
                nextX = maxW;
            }
            if (c0 != c1 && highlight != null)
            {
                g.setColor(highlight);
            }
            else
            {
                g.setColor(color0);
            }
            g.drawString(String.valueOf(c0), x+offset0, y);

            if (c0 != c1 && highlight != null)
            {
                g.setColor(highlight);
            }
            else
            {
                g.setColor(color1);
            }
            g.drawString(String.valueOf(c1), x+offset1, y+lineHeight);

            x = nextX;
        }
    }
    
    private int computePreferredHeight(int availableWidth)
    {
        FontMetrics fm = getFontMetrics(getFont());
        int lineHeight = fm.getHeight();
        int y = lineHeight;
        int x = 0;
        for (int i=0; i<array0.length; i++)
        {
            char c0 = array0[i];
            char c1 = array1[i];
            int w0 = fm.charWidth(c0);
            int w1 = fm.charWidth(c1);
            int maxW = Math.max(w0, w1) + additionalSpacing;
            int nextX = x + maxW;
            if (nextX >= availableWidth)
            {
                x = 0;
                y += lineHeight * 2;
                nextX = maxW;
            }
            x = nextX;
        }
        return y + lineHeight + fm.getDescent();
    }
    
    @Override
    public Dimension getMinimumSize()
    {
        return getPreferredSize();
    }
    
    @Override
    public Dimension getPreferredSize()
    {
        if (isPreferredSizeSet())
        {
            return super.getPreferredSize();
        }
        if (getWidth() == 0)
        {
            return super.getPreferredSize();
        }
        Dimension dim = super.getPreferredSize();
        dim.height = computePreferredHeight(getWidth());
        return dim;
    }
    
    @Override
    public Dimension getPreferredScrollableViewportSize()
    {
        return getPreferredSize();
    }

    @Override
    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
    {
        FontMetrics fm = getFontMetrics(getFont());
        int lineHeight = fm.getHeight();
        return lineHeight;
    }

    @Override
    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
    {
        return getScrollableUnitIncrement(visibleRect, orientation, direction);
    }

    @Override
    public boolean getScrollableTracksViewportWidth()
    {
        return true;
    }

    @Override
    public boolean getScrollableTracksViewportHeight()
    {
        return false;
    }
}
 
Wow...

Ich meine WOW!!!

Für mal eben rumgespielt ist das so ziemlich genau das, was ich brauche, selbst mit dem Highlighting! (Highlighting hatte ich auch im Original, für das Codebeispiel hab ich das rausgenommen 🙂)

Super herzlichen Dank Dir! Jetzt ist es leider schon ein bisschen spät, morgen werde ich das mal alles im Detail nachvollziehen und einbauen. Denke, dass mich das auch in Zukunft weiterbringen wird, da ähnliche Problemstellungen schon öfters aufgetaucht sind.

Ich meld mich dann nochmal wenns läuft 😀
 
So, ein bisschen angepasst, eingebaut, und läuft wunderbar 🙂

Letztlich war ich dann doch überrascht, wie einfach die Lösung gewesen ist. Von der FontMetrics-Klasse wusste ich bis jetzt noch gar nichts, das ist ja super cool was man damit machen kann!

Vielen Dank nochmal!
 

Neue Themen


Zurück
Oben