Question

I have a custom View in which I am drawing some text. I am using various free otf/ttf font files which are available in the assets folder

public class ProjectView extends View {
    private final Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    private void init(Context context) {
        textPaint.setStyle(Paint.Style.FILL);
        textPaint.setAntiAlias(true);

        typeface = Typeface.createFromAsset(context.getAssets(), "fonts" + File.separator + fontFileName);
        textPaint.setTypeface(typeface);
    }
}

Everything seems to be working fine except one thing: The words I am drawing get randomly messed up, meaning the letters are randomly replaced with other letters or symbols.

Here is an example:

enter image description here

The correct word is in the left image but sometimes it gets drawn like in the right image. Calling invalidate() again everything gets rendered correctly again fixes the problem.

This effect is more obvious in a ListView, it happens more often there because of the frequent redraws as I call notifyDatasetChanged() on each item click. In the adapter I use it like this:

@Override
    public View getView(int position, View convertView, ViewGroup parent) {

        View view = convertView;
        ViewHolder holder;
        if (convertView == null) {
            view = inflater.inflate(R.layout.list_item_fonts, null);

            holder = new ViewHolder();
            holder.txtFont = (TextView) view.findViewById(R.id.txtFont);
            view.setTag(holder);
        } else {
            holder = (ViewHolder) view.getTag();
        }

        //tried this two but no success
        holder.txtFont.setPaintFlags(holder.txtFont.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG | Paint.DEV_KERN_TEXT_FLAG);
        holder.txtFont.getPaint().setSubpixelText(true);

        holder.txtFont.setTextSize(TypedValue.COMPLEX_UNIT_SP, fonts.get(position).getSize());

        holder.txtFont.setTypeface(Typeface.createFromAsset(context.getAssets(), "fonts" + File.separator + font.getFileName()));
}

To be honest, I have no idea what causes this or how I could prevent it. Any help is appreciated!

Was it helpful?

Solution

You are creating the typeface every time getView is called. This is inefficient, and could cause races while loading and parsing the typeface files.

Instead have in the activity a map of all loaded typefaces, and load each one only once.

I would even try to manage the typefaces on the Application class, if many activities and views use the same typefaces.

OTHER TIPS

May be this might help somebody. I got the same problem. I solved it using lru cache.

import android.content.Context;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.text.TextPaint;
import android.text.style.MetricAffectingSpan;
import android.util.LruCache;

public class TypefaceSpan extends MetricAffectingSpan {
    private static LruCache<String, Typeface> sTypefaceCache = new LruCache<>(12);
    private Typeface mTypeface;

    public TypefaceSpan(Context context, String typefaceName) {
        mTypeface = sTypefaceCache.get(typefaceName);

        if (mTypeface == null) {
            mTypeface = Typeface.createFromAsset(context.getApplicationContext().getAssets(),
                    String.format("fonts/%s", typefaceName));
            sTypefaceCache.put(typefaceName, mTypeface);
        }
    }

    @Override
    public void updateMeasureState(TextPaint p) {
        p.setTypeface(mTypeface);
        p.setFlags(p.getFlags() | Paint.SUBPIXEL_TEXT_FLAG);
    }

    @Override
    public void updateDrawState(TextPaint tp) {
        tp.setTypeface(mTypeface);
        tp.setFlags(tp.getFlags() | Paint.SUBPIXEL_TEXT_FLAG);
    }
}

example:

private TypefaceSpan typefaceSpan;

constructor:

typefaceSpan = new TypefaceSpan(context, "name_of_font.otf");

getView:

typefaceSpan.updateDrawState(holder.title.getPaint());
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top