Task list extension


Adds support for GFM (Github-flavored markdown) task-lists:

final Markwon markwon = Markwon.builder(context)

Create a default instance of TaskListPlugin with TaskListDrawable initialized to use android.R.attr.textColorLink as primary color and android.R.attr.colorBackground as background


Create an instance of TaskListPlugin with exact color values to use:

// obtain color values
final int checkedFillColor = /* */;
final int normalOutlineColor = /* */;
final int checkMarkColor = /* */;

TaskListPlugin.create(checkedFillColor, normalOutlineColor, checkMarkColor);

Specify own drawable for a task list item:

// obtain drawable
final Drawable drawable = /* */;



Please note that custom drawable for a task list item must correctly handle state in order to display done/not-done:

public class MyTaskListDrawable extends Drawable {

    private boolean isChecked;

    public void draw(@NonNull Canvas canvas) {
        // draw accordingly to the isChecked value
    /* implementation omitted */

    protected boolean onStateChange(int[] state) {
        final boolean isChecked = contains(state, android.R.attr.state_checked);
        final boolean result = this.isChecked != isChecked;
        if (result) {
            this.isChecked = isChecked;
        return result;

    private static boolean contains(@Nullable int[] states, int value) {
        if (states != null) {
            for (int state : states) {
                if (state == value) {
                    // NB return here
                    return true;
        return false;

Task list mutation

It is possible to mutate task list item state (toggle done/not-done). But note that Markwon won't handle state change internally by any means and this change is merely a visual one. If you need to persist state of a task list item change you have to implement it yourself. This should get your started:

final Markwon markwon = Markwon.builder(context)
        .usePlugin(new AbstractMarkwonPlugin() {
            public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {

                // obtain original SpanFactory set by TaskListPlugin
                final SpanFactory origin = builder.getFactory(TaskListItem.class);
                if (origin == null) {
                    // or throw, as it's a bit weird state and we expect
                    // this factory to be present

                builder.setFactory(TaskListItem.class, new SpanFactory() {
                    public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) {
                        // it's a bit non-secure behavior and we should validate
                        // the type of returned span first, but for the sake of brevity
                        // we skip this step
                        final TaskListSpan span = (TaskListSpan) origin.getSpans(configuration, props);

                        if (span == null) {
                            // or throw
                            return null;

                        // return an array of spans
                        return new Object[]{
                                new ClickableSpan() {
                                    public void onClick(@NonNull View widget) {
                                        // toggle VISUAL state

                                        // do not forget to invalidate widget

                                        // execute your persistence logic

                                    public void updateDrawState(@NonNull TextPaint ds) {
                                        // no-op, so appearance is not changed (otherwise
                                        // task list item will look like a link)
Last Updated: 7/16/2020, 12:14:12 PM