Markwon:基于 Android 原生 Spannable 的 Markdown 渲染库(无需 WebView)完整指南

发布时间:2026/10/5 6:29:02
Markwon:基于 Android 原生 Spannable 的 Markdown 渲染库(无需 WebView)完整指南 UI组件移动开发【免费下载链接】MarkwonAndroid markdown library (no WebView)项目地址https://gitcode.com/gh_mirrors/ma/Markwon点击查看免费下载本文以仓库根文档 docs/README.md 为核心全面介绍Markwon当前仓库版本 4.6.2见 gradle.properties的设计理念、支持的 Markdown 语法全集、安装与快速上手方式并结合仓库源码与官方文档深入解析其渲染管线、插件机制、编辑器与各类扩展模块。读完本文你将掌握如何在任意 TextView 系控件、Toast 乃至 EditText 中输入输出 Markdown并学会通过插件与配置项定制渲染行为。Markwon 是一个面向 Android 的 Markdown 库它借助commonmark-java解析遵循CommonMark 规范根 README 中引用的规范版本为 0.28见 README.md的 Markdown 输入并直接将解析结果渲染为Android 原生 Spannable。渲染过程中不经过任何 HTML 中间步骤也不需要 WebView因此既快又轻且可以在所有接受Spanned内容的位置使用。本文涉及的核心实现源码位于 markwon-core扩展模块则一一对应 settings.gradle 中列出的 Gradle 子工程仓库根目录的 README.md 与 docs/README.md 为内容同源的项目总览。设计理念纯原生渲染无 WebViewMarkwon 与常见的用 WebView 加载 HTML 渲染 Markdown方案有本质区别解析严格遵循 CommonMark 规范借助 commonmark-java 完成词法、语法分析产出标准org.commonmark.node.Node语法树渲染通过访问者模式Visitor遍历节点树将每个节点映射为 Android 原生的Span最终得到Spanned文本呈现Spanned可以直接交给TextView、Button、Switch、CheckBox等所有 TextView 家族控件也可以用于Toast或其他接受Spanned的场所。从源码来看Markwon是一个抽象类Markwon.java对外提供create、builder、parse、render、toMarkdown、setMarkdown等核心 API。官方在文档中将其描述为极速extremely fast、功能丰富且可扩展feature-rich and extensible。文档中还特别强调了一个实用的默认行为默认配置使用 TextView 的 textColor 来渲染文字样式因此只要修改 TextView 的 textColor整个 Markdown 呈现风格就会随之联动变化。Markwon 浅色主题渲染截图 Markwon 深色主题渲染截图支持的 Markdown 功能全集根据 docs/README.md 与根 README.mdMarkwon 对 CommonMark 规范中的全部 Markdown 特性均有支持另有多个扩展模块补齐表格、删除线、任务列表、LaTeX 公式、语法高亮等能力类别语法/示例所属模块强调Emphasis*、_core加粗Strong emphasis**、__core标题Headers#{1,6}core链接Links[]()与[][]引用式core图片Imagesaltcore markwon-image 系列分隔线Thematic break---、***、___core引用块Quotes{1,}支持嵌套core有序/无序列表支持嵌套core行内代码Inline codecodecore代码块Code blocks围栏式代码块core markwon-syntax-highlight删除线Strike-through~~markwon-ext-strikethrough表格TablesGFM 风格存在一定限制markwon-ext-tables语法高亮Syntax highlight代码块着色markwon-syntax-highlightLaTeX 公式$...$等markwon-ext-latex任务列表Task lists- [ ]/- [X]/- [x]markwon-ext-tasklistHTML内联与块级 HTMLmarkwon-htmlHTML 支持是其中的亮点它覆盖了一组常用标签的默认渲染强调i、em、cite、dfn加粗b、strong上标/下标sup、sub下划线u、ins删除线s、strike、del链接a列表ul、ol图片img需要配置图片加载器详见下文配置章节引用块blockquote标题h1–h6任务列表的三种书写形态也由官方文档给出- [ ]未完成Not done- [X]完成Done大写 X- [x]完成小写 x可与删除线、加粗混用此外文档明确指出任何 HTML/XML 标签都可以渲染但需要开发者自定义TagHandler详细说明见 HTML 渲染文档。安装与依赖引入根 README 给出了最基础的依赖声明implementation io.noties.markwon:core:${markwonVersion}所有官方构件artifacts共享同一版本号当前仓库为4.6.2见 gradle.properties统一发布在release与snapshot仓库。正式发布版可直接从 Maven Central 获取若需要使用最新的SNAPSHOT版本则需在根项目的build.gradle中加入快照仓库见 安装文档allprojects { repositories { jcenter() google() // 快照仓库 maven { url https://oss.sonatype.org/content/repositories/snapshots/ } } }完整的构件清单如core、editor、html、image及各类集成模块由 settings.gradle 中的子工程一一对应可到 安装文档 中查看全部官方构件。快速上手三种典型用法官方入门文档 Getting started 提供了三种从最简单到最精细的用法以下代码均可直接复制运行。方式一一条命令渲染到 TextView// 获取 Markwon 实例 final Markwon markwon Markwon.create(context); // 直接设置 markdown 到 TextView markwon.setMarkdown(textView, **Hello there!**);方式二得到 Spanned 用于任意场所// 获取 Markwon 实例 final Markwon markwon Markwon.create(context); // 解析 markdown 并生成带样式的文本 final Spanned markdown markwon.toMarkdown(**Hello there!**); // 例如用于 Toast Toast.makeText(context, markdown, Toast.LENGTH_LONG).show();方式三显式 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();从 Markwon.java 源码可以看到Markwon.create(context)本质上等价于builder(context).usePlugin(CorePlugin.create()).build()即一个仅注册CorePlugin的最小可用实例如需更细的定制则使用Markwon.builder(context)。配置能力总览MarkwonConfiguration官方文档 Configuration 指出MarkwonConfiguration持有 Markwon 的公共功能配置项。其中可直接配置的属性有AsyncDrawableLoader负责图片的加载与显示4.0.0 起回归SyntaxHighlight代码块语法高亮LinkResolver响应链接点击4.0.0 起此前为LinkSpan.ResolverImageDestinationProcessor处理图片目标地址4.4.0 起此前为UrlProcessorImageSizeResolver控制图片显示尺寸而MarkwonTheme与MarkwonSpansFactory虽可从MarkwonConfiguration实例获取但必须通过 Plugin 的configureTheme/configureSpansFactory方法完成配置。图片加载AsyncDrawableLoader若未显式指定或通过插件提供图片加载器markdown 中的图片将不会显示。例如可以通过插件显式设为 no-opfinal Markwon markwon Markwon.builder(context) .usePlugin(new AbstractMarkwonPlugin() { Override public void configureConfiguration(NonNull MarkwonConfiguration.Builder builder) { builder.asyncDrawableLoader(AsyncDrawableLoader.noOp()); } }) .build();Markwon 官方提供 4 种图片加载实现markwon 自有实现支持 SVG、GIF、data uri 与android_assets基于 Picasso基于 Glide基于 Coil链接点击LinkResolver默认使用LinkResolverDef它会尝试启动一个能处理该链接的 Activity若没有任何 Activity 可处理该链接则静默忽略点击事件。开发者可完全接管点击行为final Markwon markwon Markwon.builder(this) .usePlugin(new AbstractMarkwonPlugin() { Override public void configureConfiguration(NonNull MarkwonConfiguration.Builder builder) { builder.linkResolver(new LinkResolver() { Override public void resolve(NonNull View view, NonNull String link) { // 在此响应链接点击 } }); } }) .build();注意如果目标 TextView 尚未注册任何 MovementMethodMarkwon 会自动为其应用LinkMovementMethod若希望使用自己的 MovementMethod可直接设置到 TextView或使用 MovementMethodPlugin。图片地址处理ImageDestinationProcessor未显式提供时默认使用no-op实现URL 原样保留。官方提供两个实现ImageDestinationProcessorRelativeToAbsolute将相对地址转为绝对地址。例如图片img以new ImageDestinationProcessorRelativeToAbsolute(https://github.com/noties/Markwon/raw/master/)为基准处理后最终地址变为https://github.com/noties/Markwon/raw/master/art/image.JPGImageDestinationProcessorAssets将目标指向 Android assets 目录。例如img会变成file:///android_asset/art/image.JPG仅处理不带 scheme 的 URLhttps://so.me/where.png这类带协议的地址会原样保留。图片尺寸ImageSizeResolver默认使用ImageSizeResolverDef支持三种尺寸单位%百分比相对 Canvas 宽度em相对文字大小px绝对尺寸凡不属于%或em的维度均视为绝对尺寸img width100% img width2em height10px img style{width: 100%; height: 8em;}ImageSizeResolverDef在缺少某个维度时会保持原始图片的宽高比若图片未指定任何尺寸且宽度超过 TextView Canvas 宽度则会被等比缩小以适配画布。需要特别注意的是height 维度不支持%单位——因为承载 markdown 的 TextView 高度会随内容如图片加载完成而变化缺乏稳定的参考基准。插件机制Markwon 扩展性的核心自 3.0.0 起MarkwonPlugin成为处理与渲染 markdown 的核心角色甚至核心功能本身也被抽象为CorePlugin见 CorePlugin.java因此完全可以只用一套自定义插件来使用 Markwon。注册插件的入口是Markwon.BuilderMarkwon.builder(context) // 4.0.0 起无需手动注册 CorePluginbuild 时会自动添加 .usePlugin(MyPlugin.create()) .build();若想构建一个不注册 CorePlugin的实例可使用Markwon.builderNoCore(Context)见 Markwon.java。一个插件可以在 markdown 转换的各个阶段介入详见 插件文档配置阶段通过configureRegistry 预配置、configureParsercommonmark-javaParser、configureThemeMarkwonTheme、configureConfigurationMarkwonConfiguration、configureVisitorMarkwonVisitor、configureSpansFactoryMarkwonSpansFactory等钩子定制渲染管线处理原始输入processMarkdown可在解析前改写 markdown 原文例如markdown.replaceAll(foo, bar)渲染前检查/修改 NodebeforeRender可对解析后的语法树进行访问或修改渲染后回调afterRender适合清理插件的内部状态此时修改 Node 已无意义TextView 生命周期beforeSetText设置文本前例如图片插件在此注销旧AsyncDrawableSpan以利 GC与afterSetText设置文本后例如图片插件在此调度新的 Spannable 加载见AsyncDrawableScheduler.schedule。官方文档还给出了setMarkdown底层调用的伪代码流程见 插件文档// 0. 每个插件预处理原始输入 rawInput plugins.reduce(rawInput, (input, plugin) - plugin.processMarkdown(input)); // 1. 解析为 Node node parser.parse(rawInput); // 2. 每个插件在渲染前检查/修改 Node plugins.forEach(plugin - plugin.beforeRender(node)); // 3. 访问者遍历渲染 node.accept(visitor); // 4. 渲染后回调 plugins.forEach(plugin - plugin.afterRender(node, visitor)); // 5. 得到带样式的文本 final Spanned markdown visitor.markdown(); // NB: 6-8 仅在应用到 TextView 时执行 // 6. 应用前回调 plugins.forEach(plugin - plugin.beforeSetText(textView, markdown)); // 7. 设置文本 textView.setText(markdown); // 8. 应用后回调 plugins.forEach(plugin - plugin.afterSetText(textView));典型插件用法示例注册 commonmark-java 解析器扩展例如删除线需配合 markwon-ext-strikethrough 模块final Markwon markwon Markwon.builder(context) .usePlugin(new AbstractMarkwonPlugin() { Override public void configureParser(NonNull Parser.Builder builder) { // 无需调用 super.configureParser(builder) builder.extensions(Collections.singleton(StrikethroughExtension.create())); } }) .build();定制核心主题例如代码块前景色与背景色见 MarkwonThemefinal Markwon markwon Markwon.builder(context) .usePlugin(new AbstractMarkwonPlugin() { Override public void configureTheme(NonNull MarkwonTheme.Builder builder) { builder .codeTextColor(Color.BLACK) .codeBackgroundColor(Color.GREEN); } }) .build();为Emphasis节点替换 Span 工厂将斜体改为下划线详见 Spans Factoryfinal Markwon markwon Markwon.builder(context) .usePlugin(new AbstractMarkwonPlugin() { Override public void configureSpansFactory(NonNull MarkwonSpansFactory.Builder builder) { builder.setFactory(Emphasis.class, new SpanFactory() { Override public Object getSpans(NonNull MarkwonConfiguration configuration, NonNull RenderProps props) { return new UnderlineSpan(); } }); } }) .build();SpanFactory还支持返回对象数组以对同一节点叠加多个 Span例如同时加下划线并设为红色new Object[]{ new UnderlineSpan(), new ForegroundColorSpan(Color.RED) }。若要禁用某类节点的渲染可在configureVisitor中将其访问器置空例如builder.on(Heading.class, null)即可关闭标题渲染相关机制详见 MarkwonVisitor。编辑器随输入高亮的 Markdown 编辑4.2.0自 4.2.0 起Markwon 附带 markdown 编辑器模块对应 markwon-editor 子工程可为EditText提供边输入边高亮的能力让用户在编辑时实时看到 markdown 语法着色。最简单的接入方式// 获取 Markwon 实例 final Markwon markwon Markwon.create(this); // 创建编辑器 final MarkwonEditor editor MarkwonEditor.create(markwon); // 注册文本监听触发实时高亮 editText.addTextChangedListener(MarkwonEditorTextWatcher.withProcess(editor));若输入内容较大、需要把高亮计算放到后台线程可使用withPreRendereditText.addTextChangedListener(MarkwonEditorTextWatcher.withPreRender( editor, Executors.newCachedThreadPool(), editText));MarkwonEditorTextWatcher会在EditText内容变化时自动触发高亮也可以手动调用MarkwonEditoreditor.process(editText.getText()); // 注意MarkwonEditor 在调用线程上执行如需后台执行请从后台线程调用 editor.preRender(editText.getText(), new MarkwonEditor.PreRenderResultListener() { Override public void onPreRenderResult(NonNull MarkwonEditor.PreRenderResult result) { // 建议核对结果是否仍对应同一输入例如比对原始文本 if (editText.getText().toString().equals(result.resultEditable().toString())) { // 若处于后台线程别忘了切回主线程执行 dispatch result.dispatchTo(editText.getText()); } } });官方文档明确了两点实现细节与限制实现原理高亮基于文本 diff实现——原始 markdown 输入中出现在渲染结果里缺失的部分一律视为标点符号punctuation并着色已知限制表格与 LaTeX 节点无法被正确渲染它们会整体被当作 punctuation 处理。原因在于这两类节点在实现上是模拟mocked的不会作为文本出现在最终结果中因而无法参与 diff。自定义标点 Span默认情况下编辑器用控件较浅的文字颜色来修饰标点。若要换成自定义 Span可用punctuationSpan配置final MarkwonEditor editor MarkwonEditor.builder(Markwon.create(this)) .punctuationSpan(CustomPunctuationSpan.class, CustomPunctuationSpan::new) .build();public class CustomPunctuationSpan extends ForegroundColorSpan { CustomPunctuationSpan() { super(0xFFFF0000); // 红色 } }EditHandler对输入内容做附加高亮若希望额外高亮输入中的某些片段例如让被**包裹的文本在编辑器中显示为加粗可注册EditHandler。MarkwonEditor内置StrongEmphasisSpan、BlockQuoteEditHandler、CodeEditHandler、HeadingEditHandler、LinkEditHandler、StrikethroughEditHandler等处理器示例代码位于 app-sample/src/main/assets/samples/editor/shared。以下为官方文档中的完整示例final MarkwonEditor editor MarkwonEditor.builder(Markwon.create(this)) .useEditHandler(new AbstractEditHandlerStrongEmphasisSpan() { Override public void configurePersistedSpans(NonNull PersistedSpans.Builder builder) { // 定义哪些 Span 在 EditText 中持久化文本变化时不删除而是复用按位置调整 // 相当于 Span 缓存。这里特意用 Bold 以表示它与屏幕外渲染结果中的 StrongEmphasisSpan 不是同一个 builder.persistSpan(Bold.class, Bold::new); } Override public void handleMarkdownSpan( NonNull PersistedSpans persistedSpans, NonNull Editable editable, NonNull String input, NonNull StrongEmphasisSpan span, int spanStart, int spanTextLength) { // 不能硬编码分隔符长度spanTextLength 4因为多个行内节点可能指向同一段文本。 // 例如 **_~~hey~~_** 中这里会收到 **_~~必须手动在原始输入中查找真实位置 final MarkwonEditorUtils.Match match MarkwonEditorUtils.findDelimited(input, spanStart, **, __); if (match ! null) { editable.setSpan( // 处理 StrongEmphasisSpan用 Bold 呈现在 EditText 中 // 若仍想用 StrongEmphasisSpan必须通过 persistedSpans 获取 persistedSpans.get(Bold.class), match.start(), match.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE ); } } NonNull Override public ClassStrongEmphasisSpan markdownSpanType() { return StrongEmphasisSpan.class; } }) .build();扩展生态与示例应用Markwon 的扩展能力通过 Gradle 多模块工程组织见 settings.gradle除核心外还包括markwon-editor编辑器、markwon-htmlHTML 渲染、markwon-image及其 Picasso/Glide/Coil 集成、markwon-inline-parser行内解析、markwon-linkify链接识别、markwon-recycler与markwon-recycler-tableRecyclerView 渲染、markwon-simple-ext简易扩展、markwon-syntax-highlight语法高亮、markwon-ext-*删除线/表格/任务列表/LaTeX以及markwon-test-span测试辅助。官方还提供了 示例应用内含 120 个按主题组织的使用示例源码位于 app-sample/src/main/assets/samples覆盖图片加载、HTML、表格、任务列表、编辑器、LaTeX、插件定制等场景每个示例都带可搜索列表与源码查看samples.json 记录了全部示例清单。在 docs/README.md 中还设有 Awesome Markwon 社区展示区收录使用 Markwon 的应用与第三方扩展插件并欢迎开发者提交自己的项目。版本演进与进一步阅读仓库的 CHANGELOG.md 记录了版本变更历史docs/docs/v4 目录下是面向 4.x 的完整官方文档可按需深入安装文档 与 核心入门配置详解 与 插件机制HTML 渲染、图片加载、语法高亮表格、任务列表、删除线、LaTeX编辑器 与 实战配方recipesdocs 目录中还保留了 2.x 与 3.x 的历史文档docs/docs/v2、docs/docs/v3可对照了解 API 演进脉络如 3.0.0 起 Markwon 变为实例化对象、4.0.0 起 CorePlugin 自动注册等变化。结语Markwon 以纯 Android 原生 Spannable、无需 WebView的独特路线把 CommonMark 规范与 Android 文本渲染无缝衔接从一行Markwon.create(context).setMarkdown(textView, markdown)即可完成的快速接入到插件、配置、编辑器构成的深度定制体系再到表格、LaTeX、语法高亮等丰富的扩展生态它为 Android 开发者提供了一条清晰、可验证的 Markdown 渲染与编辑路径。上述所有能力均有官方文档、源码与示例应用作为依据读者可直接在本仓库中逐一对照验证。赞分享UI组件移动开发【免费下载链接】MarkwonAndroid markdown library (no WebView)项目地址https://gitcode.com/gh_mirrors/ma/Markwon点击查看免费下载相关推荐Markwon基于 Spannable 的 Android 原生 Markdown 渲染库完全指南Markwon基于 Spannable 的 Android 原生 Markdown 渲染库完全指南 本文围绕开源仓库 Markwon https://linkUI组件移动开发Markwon Android Markdown 渲染库 v2 全解析无 WebView 的原生 Spannable 渲染方案Markwon Android Markdown 渲染库 v2 全解析无 WebView 的原生 Spannable 渲染方案 Markwon 是面向 AndUI组件移动开发Markwon 的 MarkwonView 与 MarkwonViewCompat无需 WebView 的 Android Markdown 渲染视图指南Markwon 的 MarkwonView 与 MarkwonViewCompat无需 WebView 的 Android Markdown 渲染视图指南 本UI组件移动开发上一篇如何快速构建Android TV直播频道应用基于TV Input Framework的完整指南下一篇Nix 入门配置教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考