news 2026/10/5 1:59:12

Markwon 3.x 入门指南:在 Android 中无需 WebView 将 Markdown 渲染为富文本

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Markwon 3.x 入门指南:在 Android 中无需 WebView 将 Markdown 渲染为富文本
  • UI组件
  • 移动开发

【免费下载链接】Markwon

Android markdown library (no WebView)

项目地址:https://gitcode.com/gh_mirrors/ma/Markwon
点击查看免费下载

Markwon 是一个纯 Android 原生实现的开源 Markdown 解析与渲染库,核心能力全部建立在TextView及其派生控件之上,全程不依赖 WebView。本文以 3.x 版本的入门文档为核心,介绍从安装、Markwon.create(context)快速起步,到parse/render/setParsedMarkdown显式调用链的完整用法,并结合仓库源码剖析Markwon实例内部的解析、渲染与设置文本流程,帮助你快速掌握在应用中使用 Markdown 的推荐姿势与 Plugin 扩展机制。

准备工作:将 Markwon 添加到项目

在编写任何代码之前,需要先把Markwon的依赖加入工程。官方文档要求在 安装指南 中了解具体步骤,其要点包括:

  • 所有官方 artifact 共享同一个版本号,并同时发布到release与snapshot仓库;
  • 如需体验最新SNAPSHOT版本,需要在根工程的build.gradle中追加快照仓库:
allprojects { repositories { jcenter() google() // 快照仓库 maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } } }

此外,3.x 的 Maven artifact 组已变更为ru.noties.markwon(详见 迁移说明)。完成依赖配置后,即可开始编写代码。

Quick one:三行代码在 TextView 上渲染 Markdown

最直接的用法是通过Markwon.create(context)获取实例,然后调用setMarkdown把 Markdown 字符串直接渲染到TextView上:

// 获取 Markwon 实例 final Markwon markwon = Markwon.create(context); // 设置 markdown markwon.setMarkdown(textView, "**Hello there!**");

如果不想把结果直接绑定到某个TextView,而是希望拿到渲染后的富文本对象(Spanned)用于其他场景(比如弹一个 Toast),可以使用toMarkdown:

// 获取 Markwon 实例 final Markwon markwon = Markwon.create(context); // 解析 markdown 并生成带样式的文本 final Spanned markdown = markwon.toMarkdown("**Hello there!**"); // 自由使用 Toast.makeText(context, markdown, Toast.LENGTH_LONG).show();

这里的**Hello there!**是标准的 CommonMark 加粗语法,渲染后得到的Spanned中会包含粗体 span,可以直接交给任何需要CharSequence的 Android 组件。

create 到底创建了什么

从源码看,Markwon.create(context)并非黑盒魔法,而是builder(context).usePlugin(CorePlugin.create()).build()的简写形式(见 Markwon.java):

@NonNull public static Markwon create(@NonNull Context context) { return builder(context) .usePlugin(CorePlugin.create()) .build(); }

而builder(context)本身也会预置CorePlugin(Markwon.java)。也就是说,create得到的实例是最小可用的 Markwon——只注册了CorePlugin,它负责标题、加粗、斜体、引用块、代码块、链接、列表、图片、分割线等核心 Markdown 元素的解析与 span 生成(注册逻辑见 CorePlugin.configureSpansFactory)。如果需要更多能力(如表格、删除线、HTML、语法高亮、图片加载等),应当改用Markwon.builder(context)并叠加相应插件。

3.x 迁移注意:告别静态工具方法

使用 3.x 时需要特别注意:自3.0.0起,Markwon不再提供静态工具方法,所有解析与渲染都基于「实例」进行。也就是说,2.x 中形如Markwon.setMarkdown(textView, "...")的静态调用方式已不存在,必须先把Markwon实例创建出来(如Markwon.create(context)),再调用实例方法。详细的迁移点(删除线/表格独立成模块、HTML 不再隐式启用、markwon-view模块移除、artifact 组变更等)请参考 migration-2-3.md。

Longer one:显式 parse 与 render

setMarkdown是「解析 + 渲染 + 设置文本」三合一的便捷入口。如果你需要控制中间的每一步,可以显式调用parse与render:

// 获取 Markwon 实例 final Markwon markwon = Markwon.create(context); // 把 markdown 解析成 commonmark-java 的 Node final Node node = markwon.parse("Are **you** still there?"); // 由已解析的 Node 生成带样式的文本 final Spanned markdown = markwon.render(node); // 设置到 TextView markwon.setParsedMarkdown(textView, markdown); // 或者交给 Toast Toast.makeText(context, markdown, Toast.LENGTH_LONG).show();
  • parse(String):返回 commonmark-java 的Node树,只解析、不渲染;
  • render(Node):把解析好的Node转成Spanned富文本;
  • setParsedMarkdown(TextView, Spanned):把已经渲染好的Spanned应用到TextView,并触发插件的 TextView 生命周期回调。

从 MarkwonImpl.java 可以看到setMarkdown正是两者的组合:

@Override public void setMarkdown(@NonNull TextView textView, @NonNull String markdown) { setParsedMarkdown(textView, toMarkdown(markdown)); }

render 返回的 Spanned 存在限制

render与toMarkdown直接返回的Spanned有几个固有局限(Markwon.java 的注释明确说明):

  • 图片、表格、有序列表依赖TextView才能正确显示;
  • 脱离TextView时,图片和表格大概率无法正常工作,有序列表可能出现测量偏差;
  • 因此文档建议:只要目标是把 Markdown 展示到界面上,就优先使用setMarkdown或setParsedMarkdown——这两个方法会额外调用插件的beforeSetText/afterSetText回调,为正确显示做准备工作。

例如CorePlugin会在beforeSetText中对有序列表编号进行测量(OrderedListItemSpan.measure),在afterSetText中为 TextView 自动设置LinkMovementMethod(见 CorePlugin.java),这些都是裸调用render所不具备的。

一个完整调用的底层流程

MarkwonImpl把「原始 Markdown → 富文本 → TextView」的流水线实现得很直白。以setMarkdown为例,实际发生的是(对应 MarkwonImpl.java):

  1. 预处理:遍历所有插件,依次调用plugin.processMarkdown(input)对原始输入做加工(见parse,MarkwonImpl.java);
  2. 解析:将处理后的文本交给 commonmark-javaParser,得到Node树;
  3. 渲染前钩子:逐个调用插件的beforeRender(node);
  4. 渲染:node.accept(visitor),由MarkwonVisitor遍历节点生成 span,产出Spanned;
  5. 渲染后钩子:逐个调用插件的afterRender(node, visitor);
  6. 设置文本前钩子:逐个调用插件的beforeSetText(textView, markdown);
  7. 设置文本:textView.setText(markdown, bufferType)(默认BufferType.SPANNABLE,见 MarkwonBuilderImpl.java);
  8. 设置文本后钩子:逐个调用插件的afterSetText(textView)。

其中第 6~8 步只在把结果设置给TextView时才会触发(即经由setMarkdown/setParsedMarkdown)。插件体系正是 3.x 版本「减少魔法」的关键,详见下文与 plugins.md。

No magic one:理解 Plugin 扩展机制

入门文档的第三节保留下来主要是「历史原因」:自3.0.0起,Markwon 大幅减少了内置魔法,引入了Plugin(即MarkwonPlugin)概念,让扩展默认行为变得简单且不破坏主流程。

插件机制的核心事实:

  • 即使是核心功能也被抽象成了CorePlugin,因此理论上你完全可以用一套自定义插件组合出属于自己的 Markwon(见 plugins.md);
  • MarkwonPlugin是一个接口,如果只想覆盖其中少数方法,可以直接继承AbstractMarkwonPlugin——它把所有方法都实现为空方法(见 AbstractMarkwonPlugin.java);
  • 插件可以干预的环节包括:配置 commonmark-javaParser、配置MarkwonTheme、配置图片加载器AsyncDrawableLoader、配置MarkwonConfiguration、配置MarkwonVisitor、配置MarkwonSpansFactory、配置 HTML 渲染器,以及前文提到的processMarkdown/beforeRender/afterRender/beforeSetText/afterSetText等流程回调;
  • 插件之间存在Priority依赖关系,AbstractMarkwonPlugin隐式声明了对CorePlugin的依赖,因此使用它时无需手动添加CorePlugin。

注册插件使用Markwon.builder(context):

Markwon.builder(context) .usePlugin(CorePlugin.create()) .build();

更多插件能力(Parser 扩展、主题配置、图片、配置、Visitor、Spans Factory、HTML 渲染器、Priority、节点前后处理等)请继续阅读 plugins.md。

构建时的组装顺序

MarkwonBuilderImpl.build()展示了插件被组合的完整过程(MarkwonBuilderImpl.java):

final Parser.Builder parserBuilder = new Parser.Builder(); final MarkwonTheme.Builder themeBuilder = MarkwonTheme.builderWithDefaults(context); final MarkwonConfiguration.Builder configurationBuilder = new MarkwonConfiguration.Builder(); final MarkwonVisitor.Builder visitorBuilder = new MarkwonVisitorImpl.BuilderImpl(); final MarkwonSpansFactory.Builder spanFactoryBuilder = new MarkwonSpansFactoryImpl.BuilderImpl(); for (MarkwonPlugin plugin : plugins) { plugin.configureParser(parserBuilder); plugin.configureTheme(themeBuilder); plugin.configureConfiguration(configurationBuilder); plugin.configureVisitor(visitorBuilder); plugin.configureSpansFactory(spanFactoryBuilder); }

可见插件添加的顺序会被完整保留,并贯穿整个实例的生命周期;所有插件必须先经过RegistryImpl的依赖校验与排序(preparePlugins),才能进入构建阶段。

更多实用 API 一览

除了解析渲染,Markwon实例还提供一组与插件和配置相关的查询方法(见 Markwon.java 与 MarkwonImpl.java):

  • hasPlugin(Class):判断某插件是否已注册(会连同父类一并匹配,例如查询MarkwonPlugin.class时只要存在任意插件就返回true);
  • getPlugin(Class)/requirePlugin(Class):取出指定类型插件,后者在未注册时会抛出IllegalStateException;
  • getPlugins():返回当前注册的全部插件(不可变列表);
  • configuration():获取MarkwonConfiguration实例(可从中读取MarkwonTheme、SyntaxHighlight、LinkResolver、ImageSizeResolver、MarkwonSpansFactory等,配置方式见 configuration.md);
  • Builder.bufferType(TextView.BufferType):指定setText时的缓冲区类型,默认SPANNABLE;
  • Builder.fallbackToRawInputWhenEmpty(boolean):控制当渲染结果为空时是否回退展示原始输入(如单个*这类未完成的片段)。自 4.4.0 起默认为true,此前版本为false;该逻辑在toMarkdown中实现(MarkwonImpl.java);
  • Builder.textSetter(TextSetter):自定义向TextView写入文本的方式(例如接入PrecomputedText)。

需要说明的是,getPlugin会返回最后一个匹配类型的插件(遍历过程中后者覆盖前者),因此在注册同类型插件时需留意顺序语义(MarkwonImpl.java)。

下一步:深入 3.x 各能力模块

入门文档将读者导向了更细分的专题文档,建议按需查阅:

  • plugins.md:Plugin 的完整能力清单与Priority依赖体系;
  • configuration.md:SyntaxHighlight、LinkResolver、UrlProcessor、ImageSizeResolver、MarkwonHtmlParser等可配置项及默认实现;
  • movement-method-plugin.md:链接点击的 MovementMethod 处理;
  • theme.md:核心主题配置;
  • visitor.md 与 spans-factory.md:节点访问与 span 工厂定制。

同时,仓库的 app-sample 示例工程提供了大量可运行的用法示例(如 Simple.kt),可作为学习每种 API 实际效果的参考。掌握入门 API 与插件机制后,你便可以在不引入 WebView 的前提下,把 Markdown 无缝集成进自己的 Android 界面。

  • UI组件
  • 移动开发

【免费下载链接】Markwon

Android markdown library (no WebView)

项目地址:https://gitcode.com/gh_mirrors/ma/Markwon
点击查看免费下载
上一篇:FlashKDA为什么选CHUNK=16?与FLA的64相比的3个关键考量
下一篇:CANN/asc-devkit: Conv3D Tiling结构体

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 1:56:31

MagiskBoot 解包重打包 boot.img 实战指南:两条命令改好启动镜像

MagiskBoot 解包重打包 boot.img 实战指南:两条命令改好启动镜像 【免费下载链接】Magisk The Magic Mask for Android 项目地址: https://gitcode.com/GitHub_Trending/ma/Magisk 手里攥着一个 boot.img,想给内核加个参数、塞个脚本,…

作者头像 李华