TaskRabbit is Hiring!

We’re a tight-knit team that’s passionate about building a solution that helps people by maximizing their time, talent and skills. We are actively hiring for our Engineering and Design teams. Click To Learn more

Colin Madere

Android & Ligature Icon Fonts

@ 11 May 2015

android UI


Android & Ligature Icon Fonts

To make working with image resources simpler in a variety of cases, we decided to try using an icon font at TaskRabbit across the board. This means designers don’t have to deal with or worry about generating the slew of icon resources with directory and name restrictions across DPI levels and device types needed to properly support images on Android, iOS and Web.

Icon Font: A font that instead of traditional letters contains icons as it’s characters, which then scale just like normal fonts do

Ligature Icon Font: An icon font which uses ligature names, such as ‘car’ instead of single unicode characters to denote which font (“character”) to display


The Design team tried a ligature-based icon font on the Web and found it to work well. Then we tried it on Android…

Where’s My Icon?

We tried loading icon font characters on Android using the ligature names and it appeared to work fine. However, when we tried it on an Android 4.0.x devices we got nothing. Since we wanted human-readable data for what was going to be shown, backing off to using the associated unicode characters to make it work wasn’t acceptable.

At this point, we had to decide whether to just drop support for 4.0.x devices or fall back to some generic single icon. This idea wasn’t very palatable, but since Web and iOS could use these fonts with no issues, we were a bit stuck.

Wait, that’s not the right icon!

Then we expanded our icon set and started doing more testing, we discovered that since our icon set had gotten complicated enough to have qualified names such as "car-outlined" instead of just "car", we noticed that all the Motorola-based devices we had were showing the wrong icon!

We realized those devices (and possibly others) were ignoring the dash ("-") and anything after it. This caused them to find/use "car" for "car-outlined", etc. Clearly, that was a show-stopper. We entertained changing the server to also return unicode characters but this really back-tracked from the readability aspect of choosing icons and being able to simply read what icon was expected or have people enter names for icons into a CMS.

Epiphany!

As I was toying with all these options and looking at the font-creation process the designers showed me, trying to see what our options were, I noticed that there was a JSON file in the font-creation process. The custom icon font toolchain used a JSON file to describe various font details including for each glyph: it’s unicode character (called code) and ligature name!

Adding that JSON file to our assets with the font:

{
    "icons": [
        {
            ...
            "properties": {
                "order": 621,
                "id": 2256,
                "name": "dislike-lined",
                "prevSize": 32,
                "code": 58890,
                "ligatures": "dislike-lined"
            },
            "setIdx": 0,
            "setId": 1,
            "iconIdx": 0
        }
        ...

And this system to process that into a static map:

// Trimmed exception/null checks for readability
public static void loadLigatureMap(InputStream fontJson) throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    FontFile fontFile;

    fontFile = mapper.readValue(fontJson, FontFile.class);

    ICON_MAP.clear();
    for (FontIcon fi : fontFile.getIcons()) {
        if (null != fi.getProperties() && null != fi.getProperties().getLigatures()) {
            FontProperties p = fi.getProperties();
            ICON_MAP.put(p.getLigatures(), String.valueOf(Character.toChars(p.getCode())));
        }
    }
}

public static String getIconUnicode(String ligature) {
    String unicodeString = ICON_FONT_LIGATURE_MAP.get(ligature);
    return null == unicodeString ? "" : unicodeString;
}

And building on our already-created custom text-based Views that load our custom font(s), adding the conditional for the ligature-icon font:

// Inside custom TextView class
private void init(AttributeSet attrs)
{
    TypefaceUtils.applyTextViewAttributes(this, attrs);
    if (null != attrs) {
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.TRTextView);
        String font = a.getString(R.styleable.TRTextView_font);
        a.recycle();

        if (null != font && TypefaceUtils.CustomTypeface.iconFont.name().equals(font)) {
            mIsIconFont = true;
            int attrIds[] = new int[]{android.R.attr.text};
            TypedArray tvArray = getContext().obtainStyledAttributes(attrs, attrIds);
            CharSequence textFromXml = tvArray.getText(0);
            tvArray.recycle();

            if (null != textFromXml && textFromXml.length() > 0) {
                setText(unicodeFromLigature(textFromXml));
            }
        }
    }
}

public void setText(CharSequence text, TextView.BufferType bufferType) {
    // if iconFont and text is longer than one character (not already unicode)
    if (mIsIconFont && null != text && text.length() > 1) {
        text = unicodeFromLigature(text);
    }
    super.setText(text, bufferType);
}

Conclusion

Sometimes you have to write a little custom code to do something you expect the platform to do. With a little extra work you can greatly simplify your icon usage and reduce design time for icons using something like this process and a ligature-based icon font.

Comments

Coments Loading...