Fork us on GitHub

TIP: Rich View Revisited

TIP: Rich View Revisited

TIP: Rich View Revisited

A couple of years ago I posted a tip about rich text view which worked out reasonably well. But it’s a bit outdated by now and it’s worth revisiting. During these two years we published the Facebook Clone which used this component.

In the facebook clone I did a lot of work for the rich text. This work added support for alignment and clickable hyperlinks as well as a couple of bug fixes. Following is the code I used in the Facebook Clone for the rich text.

public class RichTextView extends Container {
    private String text;
    private float fontSize = 2.6f;
    private EventDispatcher listeners = new EventDispatcher();

    private Font currentFont;
    private int currentColor = 0;
    private String currentLink;
    private Style lastCmp;
    private Font defaultFont;
    private Font boldFont;
    private Font italicFont;
    private int sizeOfSpace;

    public RichTextView() {
        init(null);
    }

    public RichTextView(String text, String uiid) {
        init(uiid);
        setText(text);
    }

    public RichTextView(String text) {
        init(null);
        setText(text);
    }

    private void init(String uiid) {
        boldFont = Font.createTrueTypeFont(NATIVE_MAIN_BOLD, fontSize);
        italicFont = Font.createTrueTypeFont(NATIVE_ITALIC_LIGHT, fontSize);
        if(uiid == null) {
            defaultFont = Font.createTrueTypeFont(NATIVE_MAIN_LIGHT,
                fontSize);
        } else {
            Style s = UIManager.getInstance().getComponentStyle(uiid);
            defaultFont = s.getFont();
            boldFont = boldFont.derive(defaultFont.getHeight(),
                Font.STYLE_BOLD);
            italicFont = italicFont.derive(defaultFont.getHeight(),
                Font.STYLE_ITALIC);
        }
        sizeOfSpace = defaultFont.charWidth(' ');
        currentFont = defaultFont;
    }

    public void setAlignment(int align) {
        ((FlowLayout)getLayout()).setAlign(align);
    }

    private void createComponent(String t) {
        if(t.indexOf(' ') > -1) {
            for(String s : StringUtil.tokenize(t, ' ')) {
                createComponent(s);
            }
            return;
        }
        Label l;
        if(currentLink != null) {
            Button b = new Button(t, "Label");
            final String currentLinkValue = currentLink;
            b.addActionListener(e -> listeners.fireActionEvent(
                    new ActionEvent(currentLinkValue)));
            l = b;
        } else {
            l = new Label(t);
        }
        Style s = l.getAllStyles();
        s.setFont(currentFont);
        s.setFgColor(currentColor);
        s.setPaddingUnit(Style.UNIT_TYPE_PIXELS);
        s.setPadding(0, 0, 0, sizeOfSpace);
        s.setMargin(0, 0, 0, 0);
        lastCmp = s;
        add(l);
    }

    public final void setText(String text) {
        this.text = text;
        removeAll();
        try {
            char[] chrs = ("<body>" + text + "</body>").toCharArray();
            new Parser().eventParser(new CharArrayReader(chrs));
        } catch(IOException err) {
            log(err);
        }
    }

    public String getText() {
        return text;
    }

    public void addLinkListener(ActionListener al) {
        listeners.addListener(al);
    }

    public void removeLinkListener(ActionListener al) {
        listeners.removeListener(al);
    }

    class Parser extends XMLParser {
        @Override
        protected void textElement(String text) {
            if(text.length() > 0) {
                if(lastCmp != null && text.startsWith(" ")) {
                    lastCmp.setPadding(0, 0, 0, sizeOfSpace);
                }
                createComponent(text);
                if(!text.endsWith(" ")) {
                    lastCmp.setPadding(0, 0, 0, 0);
                }
            }
        }

        @Override
        protected boolean startTag(String tag) {
            switch(tag.toLowerCase()) {
                case "a":
                    currentColor = 0x4267B2;
                    break;
                case "b":
                    currentFont = boldFont;
                    break;
                case "i":
                    currentFont = italicFont;
                    break;
            }
            return true;
        }

        @Override
        protected void endTag(String tag) {
            currentColor = 0;
            currentLink = null;
            currentFont = defaultFont;
        }

        @Override
        protected void attribute(
                String tag, String attributeName, String value) {
            if(tag.toLowerCase().equals("a") &&
                    attributeName.toLowerCase().equals("href")) {
                currentLink = value;
            }
        }

        @Override
        protected void notifyError(int errorId, String tag,
                String attribute, String value, String description) {
            log("Error during parsing: " + tag);
        }
    }
}
Share this Post:

Posted by Shai Almog

Shai is the co-founder of Codename One. He's been a professional programmer for over 25 years. During that time he has worked with dozens of companies including Sun Microsystems.
For more follow Shai on Twitter & github.