RichEditor
富文字編輯器
考慮到富文字編輯需要支援長文字的型別,目前大部分富文字使用自定義單個的Edittext無法實現View的複用,於是利用RecycleView為基礎實現的富文字編輯器,目前處於開發過程中
前言
對於富文字編輯器的實現,首先我們肯定會想到實現的編輯器需要支援的幾個必要特性:
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,所以回車換行和刪除就需要做非常多的邏輯情況處理,並且還涉及到樣式索引的拼接和分割,總之是一個大坑。
選中後,需要對游標,樣式的索引,樣式的清除和分割,還有樣式的重新建立和賦值,大坑啊大坑。
游標對應到對應樣式的字串時,下面的皮膚對應實時更改當前樣式,需要利用區間的邏輯判斷,對游標和樣式區間進行邏輯判斷,坑越來越多。。。
還有許多複雜的互動處理,這裡沒有展示,具體大家可以檢視原始碼。
專案地址
使用方式
這裡並沒有將工程釋出到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_INCLUSIVE
和SPAN_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作為基礎元件開發富文字編輯器,它在效能上到優勢,和原生到體驗,可以作為一種可行性方案作為參考依據。