前言
對于富文本編輯器的實現(xiàn),首先我們肯定會想到實現(xiàn)的編輯器需要支持的幾個必要特性:
1.涉及大量文字,圖片,文字樣式的展示與編輯。
2.涉及極其復雜的用戶交互。
目前Github上我所了解的富文本編輯器基本上實現(xiàn)方式基于兩種:
- 1.基于WebView拓展的富文本編輯器。
- 2.基于EditText重寫的富文本編輯器。
對于這兩種方案,這里提出一些我個人的看法。
1.WebView實現(xiàn)
首先WebView的渲染性能一個弊端所在,其次當涉及極其復雜的人機交互,WebView的實現(xiàn)起來就會比較困難。還有一點就是WebView的兼容性也是一個需要考慮的一點。
2.EditText重寫
對于重寫單個EditText,確實對于交互和文字渲染,樣式支持,都有很強的拓展性。但是考慮到會存在大量的圖片,這里就需要考慮到內(nèi)存的情況,對于EditText來說,肯定不存在View的復用,基本上有多少圖片,就要多少內(nèi)存。另一方面原生的TextView對于大量文字的渲染一直被人詬病,對此也有很多對于TextView的性能優(yōu)化的方案。
RecyclerView實現(xiàn)
所以我最終選擇使用RecyclerView作為實現(xiàn)富文本編輯器的實現(xiàn)方案。雖然有坑,但是也是一種可行性方案。(豆瓣的編輯器就是使用RecyclerView實現(xiàn))
優(yōu)點:首先RecyclerVie作為一款原生組件,對于大量UI組件的展示有非常良好的性能,其次RecyclerView的復用機制對于內(nèi)存消耗的控制提供了的很好的支持。
缺點::當然這里也不是說RecyclerView就絕對是實現(xiàn)富文本編輯器的首選方案,我在實現(xiàn)的過程中也遇到了很多大坑,這里就隨便列舉幾個:
1.焦點的控制
2.數(shù)據(jù)的拼接
3.樣式的存儲
4.光標的位置
and much more...
還好最后這些坑也找到了解決方案,所以這里分享一下這種實現(xiàn)方案,也為有需求的人提供一種可做參考的實現(xiàn)方案吧。
已實現(xiàn)功能
1.文本的粗體、斜體、下劃線、中劃線、刪除線、超鏈接、引用樣式、H1、H2、H3、H4。
2.圖片的插入和刪除
3.選中文本實時更改樣式
4.任意位置換行保持樣式。
5.兩行刪除為一行保持樣式。
6.隨光標實時顯示文字樣式到控制面板。
7.任意位置插入樣式
8.最終編輯文本轉(zhuǎn)MarkDown(有Bug~。。。。)
等。。。
實現(xiàn)效果


對于RecyclerView實現(xiàn)而言,回車對應的操作就是增加一個Model,所以回車換行和刪除就需要做非常多的邏輯情況處理,并且還涉及到樣式索引的拼接和分割,總之是一個大坑。

選中后,需要對光標,樣式的索引,樣式的清除和分割,還有樣式的重新創(chuàng)建和賦值,大坑啊大坑。

光標對應到對應樣式的字符串時,下面的面板對應實時更改當前樣式,需要利用區(qū)間的邏輯判斷,對光標和樣式區(qū)間進行邏輯判斷,坑越來越多。。。
還有許多復雜的交互處理,這里沒有展示,具體大家可以查看源碼。
項目地址
使用方式
這里并沒有將工程發(fā)布到JitPack,因為作為一款富文本編輯器,每人都有自己獨特的需求和交互方式,沒辦法做到一個富文本能夠應付所有的需求。并且由于富文本編輯器的交互邏輯確實復雜,沒辦法保證兼容到所有到交互和情況,所以這里只是盡自己可能實現(xiàn)到交互情況。
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)
//部分事件回調(diào)
public void setCallBack(onEditorEventListener callBack)
//操作面板右側(cè)空白增加自定義布局
public void setMoreOperateLayout(View view)
//異步轉(zhuǎn)義MarkDown
public void toMarkDown()
public interface onEditorCallback {
//行數(shù)量變化時
void onLineNumChange(List<RichModel> data);
//點擊操作面板的圖片添加圖片,可以使用自己項目中的圖片框架,選中后對應調(diào)用editor.addPhoto(List<String> data);方法即可
void onPhotoEvent();
//轉(zhuǎn)義MarkDown的進度回調(diào)
void onMarkDownTaskDoing(int progress, int max);
//轉(zhuǎn)義MarkDown成功
void onMarkDownTaskFinished(String markdown);
}
架構(gòu)圖

1.RichBuilder
全局單例,底層架構(gòu),幫助RichEditor整體的功能實現(xiàn)。
1.1 IPanel
實現(xiàn)類PanelBuilder包含兩個實現(xiàn)類,F(xiàn)ontParamBuilder表示字符類型的樣式,ParagraphBuilder表示段落類型的樣式。Panle和Editor的通信方式是通過底層的RichBuilder單例中的IPanel。
1.2 IAbstractFactory
抽象工程類,用于外層對span類型的創(chuàng)建。其中抽象工廠又分為ICharacterStyleFactory,IParagraphFactory,IUpdateAppearanceFactory三種span工廠,分別對應CharacterFactory(字符樣式span工廠),ParagraphFactory(段落樣式工廠),(自定義工廠未實現(xiàn))。
1.3 ISearchStrategy
搜索策略,用于對于某一段落中的span樣式的遍歷和處理,其中NormalSearch實現(xiàn)ISearchStrategy,利用常規(guī)遍歷處理(可以自定義實現(xiàn)快排或其他效率高的排序方式進行處理)
1.4 IParamManager
參數(shù)管理接口,實現(xiàn)類對應ParamManager,用于對當前樣式和預輸入樣式的對比和處理。
2.Editor
編輯器實現(xiàn)類,繼承于RecyclerView,Adapter對應RichAdapter,Model對應RichModel。
2.1 ISpanFilter
輸入過濾器,用于對輸入和刪除時的樣式處理。
SpanStep1Filter
第一級過濾器,用于處理樣式的追加和混雜時,對于SPAN_EXCLUSIVE_INCLUSIVE和SPAN_EXCLUSIVE_EXCLUSIVE的處理。
SpanStep2Filter
第二級過濾器,用于處理樣式的創(chuàng)建和保持,用于獲取當前文本所有的樣式集,并記錄樣式對應的index,保持到對應的RichModel中。
2.2 ParseAsyncTask
異步處理,用于處理將數(shù)據(jù)轉(zhuǎn)換成對應的轉(zhuǎn)換類型。
Parse
轉(zhuǎn)換接口
MarkDownParse
轉(zhuǎn)為MarkDown語法的邏輯處理,利用正則表達式。
2.3 RichModelHelper
數(shù)據(jù)處理類,用與合并樣式,處理樣式等相關(guān)的數(shù)據(jù)處理。
3.Panel
Panel表示操作面板,Panel默認使用的是EditorPanelAlpha。Panel通過RichBuilder中的IPanel實現(xiàn)和編輯器Editor的聯(lián)動。
總結(jié)
實現(xiàn)到過程中本來以為非常容易,結(jié)果實現(xiàn)到過程中發(fā)現(xiàn)坑越來越多,越來越大,但總算是將遇到的坑都找到了解決方案,從去年年底到上個月,前前后后大概開發(fā)了小半年,90多次commit也算是為大家踩踩坑,我認為以RecyclerView作為基礎(chǔ)組件開發(fā)富文本編輯器,它在性能上到優(yōu)勢,和原生到體驗,可以作為一種可行性方案作為參考依據(jù)。