基础概念目录介绍
- 01.业务需求简单介绍
- 02.实现的方案介绍
- 03.异常状态下保存状态信息
- 04.处理软键盘回删按钮逻辑
- 05.在指定位置插入图片
- 06.在指定位置插入输入文字
- 07.如果对选中文字加粗
- 08.利用Span对文字属性处理
- 09.如何设置插入多张图片
- 10.如何设置插入网络图片
- 11.如何避免插入图片OOM
- 12.如何删除图片或者文字
- 13.删除和插入图片添加动画
- 14.点击图片可以查看大图
- 15.如何暴露设置文字属性方法
- 16.文字中间添加图片注意事项
- 17.键盘弹出和收缩优化
- 18.前后台切换编辑富文本优化
- 19.生成html片段上传服务器
- 20.生成json片段上传服务器
- 21.图片上传策略问题思考
00.该控件介绍
1.1 富文本介绍
- 自定义文本控件,支持富文本,包含两种状态:编辑状态和预览状态。编辑状态中,可以对插入本地或者网络图片,可以同时插入多张有序图片和删除图片,支持图文混排,并且可以对文字内容简单操作加粗字体,设置字体下划线,支持设置文字超链接(超链接支持跳转),还可以统计富文本中的字数,功能正在开发中和完善中……
1.2 富文本效果图
![](https://oss.jqhtml.com/wp-content/uploads/2020/7/aQN7Nj.jpeg)
![](https://oss.jqhtml.com/wp-content/uploads/2020/7/NVZfmi.jpeg)
![](https://oss.jqhtml.com/wp-content/uploads/2020/7/aQjQBj.jpeg)
![](https://oss.jqhtml.com/wp-content/uploads/2020/7/RJ7RBv.jpeg)
![](https://oss.jqhtml.com/wp-content/uploads/2020/7/maIj6r.jpeg)
1.3 富文本开源库
01.业务需求简单介绍
- 富文本控件支持动态插入文字,图片等图文混排内容。图片可以支持本地图片,也支持插入网络链接图片;
- 富文本又两种状态:编辑状态 + 预览状态 。两种状态可以相互进行切换;
- 富文本在编辑状态,可以同时选择插入超过一张以上的多张图片,并且可以动态设置图片之间的top间距;
- 在编辑状态,支持利用光标删除文字内容,同时也支持用光标删除图片;
- 在编辑状态,插入图片后,图片的宽度填充满手机屏幕的宽度,然后高度可以动态设置,图片是剧中裁剪显示;
- 在编辑状态,插入图片后,如果本地图片过大,要求对图片进行质量压缩,大小压缩;
- 在编辑状态,插入多张图片时,添加插入过渡动画,避免显示图片生硬。结束后,光标移到插入图片中的最后一行显示;
- 编辑状态中,图片点击暴露点击事件接口,可以在4个边角位置动态设置一个删除图片的功能,点击删除按钮则删除图片;
- 连续插入多张图片时,比如顺序1,2,3,注意避免出现图片插入顺序混乱的问题(异步插入多张图片可能出现顺序错乱问题);
- 在编辑富文本状态的时候,连续多张图片之间插入输入框,方便在图片间输入文本内容;
- 在编辑状态中,可以设置文字大小和颜色,同时做好拓展需求,后期可能添加文本加粗,下划线,插入超链接,对齐方式等功能;
- 编辑状态,连续插入多张图片,如果想在图片中间插入文字内容,则需要靠谱在图片之间预留编辑文本控件,方便操作;
- 支持对文字选中的内容进行设置加粗,添加下划线,改变颜色,设置对齐方式等等;
- 关于富文本字数统计,由于富文本中包括文字和图片,因此图片和文字数量统计分开。参考易车是:共n个文字,共n个图片显示
02.实现的方案介绍
2.0 页面构成分析
-
整个界面的要求
- 整体界面可滚动,可以编辑,也可以预览
- 内容可编辑可以插入文字、图片等。图片提供按钮操作
- 软键盘删除键可删除图片,也可以删除文字内容
- 文字可以修改属性,比如加粗,对齐,下划线
-
根据富文本作出以下分析
- 使用原生控件,可插入图片、文字界面不能用一个EditText来做,需要使用LinearLayout添加不同的控件,图片部分用ImageView,界面可滑动最外层使用ScrollView。
- 使用WebView+js+css方式,富文本格式用html方式展现,比较复杂,对标签要非常熟悉才可以尝试使用
-
使用原生控件多焦点问题分析
- 界面是由多个输入区域拼接而成,暂且把输入区域称为EditText,图片区域称为ImageView,外层是LinearLayout。
-
如果一个富文本是:文字1+图片1+文字2+文字3+图片3+图片4;那么使用LinearLayout包含多个EditText实现的难点:
- 如何处理记录当前的焦点区域
- 如何处理在文字区域的中间位置插入ImageView样式的拆分和合并
- 如何处理输入区域的删除键处理
2.2 第一种方案
- 使用ScrollView作为最外层,布局包含LineaLayout,图文混排内容,则是用TextView/EditText和ImageView去填充。
- 富文本编辑状态:ScrollView + LineaLayout + n个EditText+Span + n个ImageView
- 富文本预览状态:ScrollView + LineaLayout + n个TextView+Span + n个ImageView
- 删除的时候,根据光标的位置,如果光标遇到是图片,则可以用光标删除图片;如果光标遇到是文字,则可以用光标删除文字
- 当插入或者删除图片的时候,可以添加一个过渡动画效果,避免直接生硬的显示。如何在ViewGroup中添加view,删除view时给相应view和受影响的其他view添加动画,不太容易做。如果只是对受到影响的view添加动画,可以通过设置view的高度使之显示和隐藏,还可以利用ScrollView通过滚动隐藏和显示动画,但其他受影响的view则比较难处理,最终选择布局动画LayoutTransition 就可以很好地完成这个功能。
2.3 第二种方法
- 使用WebView实现编辑器,支持n多格式,例如常见的html或者markdown格式。利用html标签对富文本处理,这种方式就需要专门处理标签的样式。
- 注意这种方法的实现,需要深入研究js,css等,必须非常熟悉才可以用到实际开发中,可以当作学习一下。这种方式对于图片的显示和上传,相比原生要麻烦一些。
2.4 富文本支持功能
- 支持加粗、斜体、删除线、下划线行内样式,一行代码即可设置文本span属性,十分方便
- 支持添加单张或者多张图片,并且插入过渡动画友好,同时可以保证插入图片顺序
- 支持富文本编辑状态和预览状态的切换,支持富文本内容转化为json内容输出,转化为html内容输出
- 支持设置富文本的文字大小,行间距,图片和文本间距,以及插入图片的宽和高的属性
- 图片支持点击预览,支持点击叉号控件去除图片,暴露给外部开发者调用。同时加载图片的逻辑也是暴露给外部开发者,充分解耦
- 关于富文本字数统计,由于富文本中包括文字和图片,因此图片和文字数量统计分开。参考易车是:共n个文字,共n个图片显示
03.异常状态下保存状态信息
-
对于自定义View,如果页面出现异常导致自定义View异常退出,则当然希望保存一些重要的信息。自定义保存状态类,继承BaseSavedState,代码如下所示
public class TextEditorState extends View.BaseSavedState { public int rtImageHeight; public static final Creator<TextEditorState> CREATOR = new Creator<TextEditorState>() { @Override public TextEditorState createFromParcel(Parcel in) { return new TextEditorState(in); } @Override public TextEditorState[] newArray(int size) { return new TextEditorState[size]; } }; public TextEditorState(Parcelable superState) { super(superState); } public TextEditorState(Parcel source) { super(source); rtImageHeight = source.readInt(); } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeInt(rtImageHeight); } }
-
如何使用该保存状态栏,自定义View中,有两个特别的方法,分别是onSaveInstanceState和onRestoreInstanceState,具体逻辑如下所示
/** * 保存重要信息
*/
@Nullable
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
TextEditorState viewState = new TextEditorState(superState);
viewState.rtImageHeight = rtImageHeight;
return viewState;
}
/**
* 复现
* @param state state
*/
@Override
protected void onRestoreInstanceState(Parcelable state) {
TextEditorState viewState = (TextEditorState) state;
rtImageHeight = viewState.rtImageHeight;
super.onRestoreInstanceState(viewState.getSuperState());
requestLayout();
}
```
04.处理软键盘回删按钮逻辑
- 想了一下,当富文本处于编辑的状态,利用光标可以进行删除插入点之前的字符。删除的时候,根据光标的位置,如果光标遇到是图片,则可以用光标删除图片;如果光标遇到是文字,则可以用光标删除文字。
- 更详细的来说,监听删除键的点击的逻辑需要注意,当光标在EditText 输入中间,点击删除不进行处理正常删除;当光标在EditText首端,判断前一个控件,如果是图片控件,删除图片控件,如果是输入控件,删除当前控件并将输入区域合并成一个输入区域。
-
创建一个键盘退格监听事件,代码如下所示:
// 初始化键盘退格监听,主要用来处理点击回删按钮时,view的一些列合并操作 keyListener = new OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) {