Markdown support in Android TextView

android markdown
markdown4j
markdown editor
structured markdown
best markdown flavor
android studio markdown preview
cmark
commonmark example

Is there a way to enable a TextView to detect markdown tags and render the text accordingly? More specifically, my app contains a TextView in which the users can provide a description, and often they will use markdown to format their description. Unfortunately the text doesn't render, and instead we see all the tags written out in the textview.

There's no built-in support for Markdown in the Android SDK. You'll have to use a lib like markdown4j or CommonMark.

noties/Markwon: Android markdown library (no WebView), Is there a way to enable a TextView to detect markdown tags and render the text accordingly? More specifically, my app contains a TextView in which the users  There is no inherit support for markdown in textview, however if you only need simple markdown-lite implementation via simple “regexp” matching, this section from my “load readme from project root folder” in https://github.com/mofosyne/instantReadmeApp would help.

There is no inherit support for markdown in textview, however if you only need simple markdown-lite implementation via simple "regexp" matching, this section from my "load readme from project root folder" in https://github.com/mofosyne/instantReadmeApp would help.

Note that this does not remove the markup in the text, only styles the lines differently. This may be a good or bad thing, depending on your application.

Oh and the nice thing? It styles in native textview, so the text is still selectable like normal text.

Specifically this line: https://github.com/mofosyne/instantReadmeApp/blob/master/app/src/main/java/com/github/mofosyne/instantreadme/ReadMe.java#L137

Slightly modified below: private void updateMainDisplay(String text) to private void style_psudomarkdown_TextView(String text, TextView textview_input), so you could use the same function for different textviews

```

/*
    Text Styler
    A crappy psudo markdown styler. Could do with a total revamp.
 */

/*
* Styling the textview for easier readability
* */
private void style_psudomarkdown_TextView(String text, TextView textview_input) {
    //TextView mTextView = (TextView) findViewById(R.id.readme_info);
    TextView mTextView = textview_input;

    // Let's update the main display
    // Needs to set as spannable otherwise http://stackoverflow.com/questions/16340681/fatal-exception-string-cant-be-cast-to-spannable
    mTextView.setText(text, TextView.BufferType.SPANNABLE);
    // Let's prettify it!
    changeLineinView_TITLESTYLE(mTextView, "# ", 0xfff4585d, 2f); // Primary Header
    changeLineinView(mTextView, "\n# ", 0xFFF4A158, 1.5f); // Secondary Header
    changeLineinView(mTextView, "\n## ", 0xFFF4A158, 1.2f); // Secondary Header
    changeLineinView(mTextView, "\n---", 0xFFF4A158, 1.2f); // Horizontal Rule
    changeLineinView(mTextView, "\n>",   0xFF89e24d, 0.9f); // Block Quotes
    changeLineinView(mTextView, "\n - ", 0xFFA74DE3, 1f);   // Classic Markdown List
    changeLineinView(mTextView, "\n- ", 0xFFA74DE3, 1f);   // NonStandard List

    //spanSetterInView(String startTarget, String endTarget, int typefaceStyle, String fontFamily,TextView tv, int colour, float size)
    // Limitation of spanSetterInView. Well its not a regular expression... so can't exactly have * list, and *bold* at the same time.
    spanSetterInView(mTextView, "\n```\n", "\n```\n",   Typeface.BOLD,        "monospace",  0xFF45c152,  0.8f, false); // fenced code Blocks ( endAtLineBreak=false since this is a multiline block operator)
    spanSetterInView(mTextView,   " **"  ,     "** ",   Typeface.BOLD,        "",  0xFF89e24d,  1f, true); // Bolding
    spanSetterInView(mTextView,    " *"  ,      "* ",   Typeface.ITALIC,      "",  0xFF4dd8e2,  1f, true); // Italic
    spanSetterInView(mTextView,  " ***"  ,    "*** ",   Typeface.BOLD_ITALIC, "",  0xFF4de25c,  1f, true); // Bold and Italic
    spanSetterInView(mTextView,    " `"  ,      "` ",   Typeface.BOLD,        "monospace",  0xFF45c152,  0.8f, true); // inline code
    spanSetterInView(mTextView, "\n    " ,      "\n",   Typeface.BOLD,        "monospace",  0xFF45c152,  0.7f, true); // classic indented code
}

private void changeLineinView(TextView tv, String target, int colour, float size) {
    String vString = (String) tv.getText().toString();
    int startSpan = 0, endSpan = 0;
    //Spannable spanRange = new SpannableString(vString);
    Spannable spanRange = (Spannable) tv.getText();
    while (true) {
        startSpan = vString.indexOf(target, endSpan-1);     // (!@#$%) I want to check a character behind in case it is a newline
        endSpan = vString.indexOf("\n", startSpan+1);       // But at the same time, I do not want to read the point found by startSpan. This is since startSpan may point to a initial newline.
        ForegroundColorSpan foreColour = new ForegroundColorSpan(colour);
        // Need a NEW span object every loop, else it just moves the span
        // Fix: -1 in startSpan or endSpan, indicates that the indexOf has already searched the entire string with not valid match (Lack of endspan check, occoured because of the inclusion of endTarget, which added extra complications)
        if ( (startSpan < 0) || ( endSpan < 0 ) ) break;// Need a NEW span object every loop, else it just moves the span
        // Need to make sure that start range is always smaller than end range. (Solved! Refer to few lines above with (!@#$%) )
        if (endSpan > startSpan) {
            //endSpan = startSpan + target.length();
            spanRange.setSpan(foreColour, startSpan, endSpan, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            // Also wannna bold the span too
            spanRange.setSpan(new RelativeSizeSpan(size), startSpan, endSpan, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            spanRange.setSpan(new StyleSpan(Typeface.BOLD), startSpan, endSpan, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
    }
    tv.setText(spanRange);
}

private void changeLineinView_TITLESTYLE(TextView tv, String target, int colour, float size) {
    String vString = (String) tv.getText().toString();
    int startSpan = 0, endSpan = 0;
    //Spannable spanRange = new SpannableString(vString);
    Spannable spanRange = (Spannable) tv.getText();
    /*
    * Had to do this, since there is something wrong with this overlapping the "##" detection routine
    * Plus you only really need one title.
     */
    //while (true) {
    startSpan = vString.substring(0,target.length()).indexOf(target, endSpan-1); //substring(target.length()) since we only want the first line
    endSpan = vString.indexOf("\n", startSpan+1);
    ForegroundColorSpan foreColour = new ForegroundColorSpan(colour);
    // Need a NEW span object every loop, else it just moves the span
        /*
        if (startSpan < 0)
            break;
            */
    if ( !(startSpan < 0) ) { // hacky I know, but its to cater to the case where there is no header text
        // Need to make sure that start range is always smaller than end range.
        if (endSpan > startSpan) {
            //endSpan = startSpan + target.length();
            spanRange.setSpan(foreColour, startSpan, endSpan, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            // Also wannna bold the span too
            spanRange.setSpan(new RelativeSizeSpan(size), startSpan, endSpan, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            spanRange.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), startSpan, endSpan, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
    }
    //}
    tv.setText(spanRange);
}


private void spanSetterInView(TextView tv, String startTarget, String endTarget, int typefaceStyle, String fontFamily, int colour, float size, boolean endAtLineBreak) {
    String vString = (String) tv.getText().toString();
    int startSpan = 0, endSpan = 0;
    //Spannable spanRange = new SpannableString(vString);
    Spannable spanRange = (Spannable) tv.getText();
    while (true) {
        startSpan = vString.indexOf(startTarget, endSpan-1);     // (!@#$%) I want to check a character behind in case it is a newline
        endSpan = vString.indexOf(endTarget, startSpan+1+startTarget.length());     // But at the same time, I do not want to read the point found by startSpan. This is since startSpan may point to a initial newline. We also need to avoid the first patten matching a token from the second pattern.
        // Since this is pretty powerful, we really want to avoid overmatching it, and limit any problems to a single line. Especially if people forget to type in the closing symbol (e.g. * in bold)
        if (endAtLineBreak){
            int endSpan_linebreak = vString.indexOf("\n", startSpan+1+startTarget.length());
            if ( endSpan_linebreak < endSpan ) { endSpan = endSpan_linebreak; }
        }
        // Fix: -1 in startSpan or endSpan, indicates that the indexOf has already searched the entire string with not valid match (Lack of endspan check, occoured because of the inclusion of endTarget, which added extra complications)
        if ( (startSpan < 0) || ( endSpan < 0 ) ) break;// Need a NEW span object every loop, else it just moves the span
        // We want to also include the end "** " characters
        endSpan += endTarget.length();
        // If all is well, we shall set the styles and etc...
        if (endSpan > startSpan) {// Need to make sure that start range is always smaller than end range. (Solved! Refer to few lines above with (!@#$%) )
            spanRange.setSpan(new ForegroundColorSpan(colour), startSpan, endSpan, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            spanRange.setSpan(new RelativeSizeSpan(size), startSpan, endSpan, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            spanRange.setSpan(new StyleSpan(typefaceStyle), startSpan, endSpan, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            // Default to normal font family if settings is empty
            if( !fontFamily.equals("") )  spanRange.setSpan(new TypefaceSpan(fontFamily), startSpan, endSpan, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
    }
    tv.setText(spanRange);
}

```

The above implementation supports only up to 2 headers (but you can easily modify the regexp to support more than 2 level headers).

It is a series of regexp based text view consisting of two functions for regexp that matches always a line changeLineinView() and changeLineinView_TITLESTYLE()

For multiline spanning spanSetterInView() function deals with it.

So extending it to fit your purpose as long as you have a regexp that doesn't clash with any other syntax would be possible.

Markdownish Syntax:

This is the supported syntax. Can't support full markdown, since this is only a lite hacky implementation. But kind handy for a no frills display that is easy to type on a mobile phone keypad.

# H1 only in first line (Due to technical hacks used)

## H2 headers as usual

## Styling
Like: *italic* **bold** ***bold_italic***

## Classic List
 - list item 1
 - list item 2

## Nonstandard List Syntax
- list item 1
- list item 2

## Block Quotes
> Quoted stuff

## codes
here is inline `literal` codes. Must have space around it.

    ```
    codeblocks
    Good for ascii art
    ```

    Or 4 space code indent like classic markdown.

Markdown support in Android TextView, MarkdownView is a simple library that helps you display Markdown text or files on Android as an HTML page just like Github. It's really simple to  It worked by parsing the markdown into html, then using the custom renderer to display the html string in a TextView for Android and a UILabel for iOS. However, we are not always using Xamarin.Forms, so let’s take a look at achieving the same functionality with just Xamarin.Android. We’ll break it down into a few parts: Parse a markdown string into an html string.

Displaying MD or Markdown files and text in Android, WebView implementation thats supports customization and provides a way to It gives ability to display markdown in all TextView widgets (TextView, Button,  Markwon. Markwon is a markdown library for Android. It parses markdown following commonmark-spec with the help of amazing commonmark-java library and renders result as Android-native Spannables. No HTML is involved as an intermediate step. No WebView is required. It's extremely fast, feature-rich and extensible.

I can recommend MarkdownView. I use it for loading markdown files from the assets folder.

In case it helps anyone, here's my implementation...

In my layout:

<us.feras.mdv.MarkdownView
    android:id="@+id/descriptionMarkdownView"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginTop="10dp"
    app:layout_constraintTop_toBottomOf="@id/thumbnailImageView"
    app:layout_constraintStart_toEndOf="@id/guidelineStart"
    app:layout_constraintEnd_toEndOf="@id/guidelineEnd"
    app:layout_constraintBottom_toTopOf="@id/parent"/>

In my Activity:

val cssPath = "file:///android_asset/markdown.css"
val markdownPath = "file:///android_asset/markdown/filename.md"
descriptionMarkdownView.loadMarkdownFile(markdownPath, cssPath)

Markdown - A categorized directory of , Markwon is a library for Android that renders markdown as system-native Spannables. It gives ability to display markdown in all TextView widgets (​TextView, Button, All markdown features are supported (including limited support for inlined  RxMarkdown is an Android library that helps to display simple markdown text in android.widget.EditText or android.widget.TextView, at same time, it supports code high light . It is backed by RxJava, implementing complicated APIs as handy reactive observables.

Take a look at the commonmark-java library. I haven't tried that myself but I think you might be able to make it work in your case

Markwon, Android markdown library based on commonmark specification that renders It parses markdown following commonmark spec with the help of amazing It gives ability to display markdown in all TextView widgets (TextView,  Markwon. Markwon is a markdown library for Android. It parses markdown following commonmark-spec with the help of amazing commonmark-java library and renders result as Android-native Spannables. No HTML is involved as an intermediate step. No WebView is required. It's extremely fast, feature-rich and extensible.

Introduction, SourceForge uses markdown syntax everywhere to allow you to create rich text markup, and extends markdown in several ways to allow for quick linking Markwon is a library for Android that renders markdown as system-native Spannables. It gives ability to display markdown in all TextView widgets (TextView, Button, Switch, CheckBox, etc), Notifications, Toasts, etc. <u>No WebView is required</u>. Library provides reasonable defaults for display style of markdown but also gives all the means to tweak the appearance if desired.

android-textview-usage / Wiki / Markdown Syntax, CWAC-AndDown: Markdown for Android (and a plea for help) or by using Html.​fromHtml() to create a Spannable for use with a TextView . TextView With Example In Android Studio. In Android, TextView displays text to the user and optionally allows them to edit it programmatically. TextView is a complete text editor, however basic class is configured to not allow editing but we can edit it.

CWAC-AndDown: Markdown for Android (and a plea for help), RxMarkdown is an Android library that helps to display simple markdown text in android.widget.EditText or android.widget.TextView . It is backed by RxJava,  Markwon is a library for Android that renders markdown as system-native Spannables. It gives ability to display markdown in all TextView widgets (TextView, Button, Switch, CheckBox, etc), Notifications, Toasts, etc. No WebView is required. Library provides reasonable defaults for display style of markdown but also gives all the means to tweak the appearance if desired.

Comments
  • Could you please add some of your code. This would help us detecting the problem and you will more likely get an answer
  • uncodin.github.io/bypass, though not supported by gradle build at the moment, I'm afraid, because it's an "apklib".
  • Anyone these provide customisations? as in font color of all attributes etc?
  • Markwon allows quite a few customization.
  • Hi @bwt, I trying the Markwon library in my app but I am stuck with the link handling part. I am wondering how to retrieve the link text for further formatting. Is there a place where I can get more info on using Markwon library? Any help is much appreciated.
  • Thanks to you comment, I got in touch with this library this weekend and it's pretty easty to deal with it. In my case, I used it to view notes with Markdown formatting and it worked well enough to do the task.