RichEditor——一款基于RecyclerView实现的富文本编辑器实现方案(支持图文、转义生成MarkDown、粗体、斜体、下划线、删除线、超链接、标题等)

前言

对于富文本编辑器的实现,首先我们肯定会想到实现的编辑器需要支持的几个必要特性:

1.涉及大量文字,图片,文字样式的展示与编辑。
2.涉及极其复杂的用户交互。

目前Github上我所了解的富文本编辑器基本上实现方式基于两种:

  • 1.基于WebView拓展的富文本编辑器。
  • 2.基于EditText重写的富文本编辑器。

对于这两种方案,这里提出一些我个人的看法。

1.WebView实现

首先WebView的渲染性能一个弊端所在,其次当涉及极其复杂的人机交互,WebView的实现起来就会比较困难。还有一点就是WebView的兼容性也是一个需要考虑的一点。

2.EditText重写

对于重写单个EditText,确实对于交互和文字渲染,样式支持,都有很强的拓展性。但是考虑到会存在大量的图片,这里就需要考虑到内存的情况,对于EditText来说,肯定不存在View的复用,基本上有多少图片,就要多少内存。另一方面原生的TextView对于大量文字的渲染一直被人诟病,对此也有很多对于TextView的性能优化的方案。

RecyclerView实现

所以我最终选择使用RecyclerView作为实现富文本编辑器的实现方案。虽然有坑,但是也是一种可行性方案。(豆瓣的编辑器就是使用RecyclerView实现)
优点:首先RecyclerVie作为一款原生组件,对于大量UI组件的展示有非常良好的性能,其次RecyclerView的复用机制对于内存消耗的控制提供了的很好的支持。
缺点::当然这里也不是说RecyclerView就绝对是实现富文本编辑器的首选方案,我在实现的过程中也遇到了很多大坑,这里就随便列举几个:
1.焦点的控制
2.数据的拼接
3.样式的存储
4.光标的位置
and much more...
还好最后这些坑也找到了解决方案,所以这里分享一下这种实现方案,也为有需求的人提供一种可做参考的实现方案吧。

已实现功能

1.文本的粗体、斜体、下划线、中划线、删除线、超链接、引用样式、H1、H2、H3、H4。
2.图片的插入和删除
3.选中文本实时更改样式
4.任意位置换行保持样式。
5.两行删除为一行保持样式。
6.随光标实时显示文字样式到控制面板。
7.任意位置插入样式
8.最终编辑文本转MarkDown(有Bug~。。。。)
等。。。

实现效果

效果图

样式拼接

对于RecyclerView实现而言,回车对应的操作就是增加一个Model,所以回车换行和删除就需要做非常多的逻辑情况处理,并且还涉及到样式索引的拼接和分割,总之是一个大坑。

样式选中修改

选中后,需要对光标,样式的索引,样式的清除和分割,还有样式的重新创建和赋值,大坑啊大坑。

样式同步

光标对应到对应样式的字符串时,下面的面板对应实时更改当前样式,需要利用区间的逻辑判断,对光标和样式区间进行逻辑判断,坑越来越多。。。

还有许多复杂的交互处理,这里没有展示,具体大家可以查看源码。

项目地址

RichEditor

使用方式

这里并没有将工程发布到JitPack,因为作为一款富文本编辑器,每人都有自己独特的需求和交互方式,没办法做到一个富文本能够应付所有的需求。并且由于富文本编辑器的交互逻辑确实复杂,没办法保证兼容到所有到交互和情况,所以这里只是尽自己可能实现到交互情况。

1.引入editor的lib到工程
2.xml加入编辑器

<com.study.xuan.editor.widget.Editor
        android:id="@+id/editor"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

就可以正常使用了。

进阶

简单到封装了一下使用利用RichHelper

//绑定xml中的Editor
public void attach(Editor editor);
//new 一个Editor
public Editor buildEditor(Context context)
//部分事件回调
public void setCallBack(onEditorEventListener callBack)
//操作面板右侧空白增加自定义布局
public void setMoreOperateLayout(View view)
//异步转义MarkDown
public void toMarkDown()
public interface onEditorCallback { 
    //行数量变化时
    void onLineNumChange(List<RichModel> data);
    //点击操作面板的图片添加图片,可以使用自己项目中的图片框架,选中后对应调用editor.addPhoto(List<String> data);方法即可
    void onPhotoEvent();
    //转义MarkDown的进度回调
    void onMarkDownTaskDoing(int progress, int max);
    //转义MarkDown成功
    void onMarkDownTaskFinished(String markdown);
}

架构图

架构图

1.RichBuilder

全局单例,底层架构,帮助RichEditor整体的功能实现。

1.1 IPanel

实现类PanelBuilder包含两个实现类,FontParamBuilder表示字符类型的样式,ParagraphBuilder表示段落类型的样式。Panle和Editor的通信方式是通过底层的RichBuilder单例中的IPanel。

1.2 IAbstractFactory

抽象工程类,用于外层对span类型的创建。其中抽象工厂又分为ICharacterStyleFactory,IParagraphFactory,IUpdateAppearanceFactory三种span工厂,分别对应CharacterFactory(字符样式span工厂),ParagraphFactory(段落样式工厂),(自定义工厂未实现)。

1.3 ISearchStrategy

搜索策略,用于对于某一段落中的span样式的遍历和处理,其中NormalSearch实现ISearchStrategy,利用常规遍历处理(可以自定义实现快排或其他效率高的排序方式进行处理)

1.4 IParamManager

参数管理接口,实现类对应ParamManager,用于对当前样式和预输入样式的对比和处理。

2.Editor

编辑器实现类,继承于RecyclerView,Adapter对应RichAdapter,Model对应RichModel。

2.1 ISpanFilter

输入过滤器,用于对输入和删除时的样式处理。
SpanStep1Filter
第一级过滤器,用于处理样式的追加和混杂时,对于SPAN_EXCLUSIVE_INCLUSIVESPAN_EXCLUSIVE_EXCLUSIVE的处理。
SpanStep2Filter
第二级过滤器,用于处理样式的创建和保持,用于获取当前文本所有的样式集,并记录样式对应的index,保持到对应的RichModel中。

2.2 ParseAsyncTask

异步处理,用于处理将数据转换成对应的转换类型。
Parse
转换接口
MarkDownParse
转为MarkDown语法的逻辑处理,利用正则表达式。

2.3 RichModelHelper

数据处理类,用与合并样式,处理样式等相关的数据处理。

3.Panel

Panel表示操作面板,Panel默认使用的是EditorPanelAlpha。Panel通过RichBuilder中的IPanel实现和编辑器Editor的联动。

总结

实现到过程中本来以为非常容易,结果实现到过程中发现坑越来越多,越来越大,但总算是将遇到的坑都找到了解决方案,从去年年底到上个月,前前后后大概开发了小半年,90多次commit也算是为大家踩踩坑,我认为以RecyclerView作为基础组件开发富文本编辑器,它在性能上到优势,和原生到体验,可以作为一种可行性方案作为参考依据。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342