Implement Meme Generator With StaticLayout

Two months ago I started writing a meme generator as a side project, what I want to do is not just adding two lines of text on image but a more generic text-on-image solution.

StaticLayout

When implementing custom views, we can use canvas.drawText to display text, but if you want to draw text in multiple lines, a straight forward way is to calculate the y position of each line and draw them one by one. One problem with this method is that, you have to do the line splitting yourself and it seems lots of work.

StaticLayout is a less well-known class in the framework, it is not included in any API Guides or Training documentation.

This is used by widgets to control text layout. You should not need to use this class directly unless you are implementing your own widget or custom display object, or would be tempted to call Canvas.drawText() directly.

But if you take a quick look of inside TextView (like 5000 lines, I have not read it in detail), you will find that StaticLayout is used to do the text layout.

Meme generator structure

RendererView

A custom view that extends ImageView to provide text on image drawing ability, the actually text drawing logic is implemented in the renderer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class RendererView extends ImageView {
    private Renderer mRenderer;

    // fill in constrcutor

    public void setRenderer(MemeRenderer renderer) {
        mRenderer = renderer;
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mRenderer != null) {
            mRenderer.draw(canvas);
        }

    }
}

TextGroup

A basic TextGroup manages the text, Paint and StaticLayout object.

To draw text with stroke, we need to draw it twice, first draw the stroke (using Paint.Style.STROKE), then draw the text over it. I use a wrapper TextGroup interface to wrap the text and the stroke layout, and can extend to arbitrary number of text layer.

Renderer

Renderer implements a draw(canvas) function to draw text on canvas. In the case of meme generator, the renderer contains two TextGroup, top and bottom. In the draw function, we translate to proper position and run the draw function implemented in the TextGroup.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public void draw(Canvas canvas) {

    // some calculation

    canvas.save();
    canvas.translate(x, topY);
    mTopGroup.draw(canvas);
    canvas.restore();

    canvas.save();
    canvas.translate(x, bottomY);
    mBottomGroup.draw(canvas);
    canvas.restore();

}

AutoResizer

With the help of StaticLayout, you can get the line count of your current layout and can adjust your text size to provide an auto resize function.

Play store submission

When I tried to upload my meme generator demo to play store, the new review system rejected my submission as some meme may have copyright issue… So I just changed my images to some free stock photo to get pass the review process.

Here is my demo app

Future work

With the structure I have, I suppose it is easy to modify the app to provide different style/layout and create use case other than meme generator. I have few ideas in mind but that may require other data api and image source to make it works.

Reference

Comments