Android富文本编辑器
如果你正在寻找一个优秀的Android富文本编辑器,千万不要错过这个!
另外,我目前正在寻找工作机会。如果你有合适的职位空缺,欢迎通过chinalwb168@gmail.com与我联系。谢谢!
该项目目前仍在开发中,欢迎fork并加入我!
我已将颜色选择器和表情选择器发布为独立组件,以便在其他项目中轻松重用。
各位好!如果你正在阅读这段文字,我相信你正在为Android上的富文本编辑器寻找解决方案,特别是希望使用Android原生API来实现。如果我猜对了,也许我可以帮上忙。但是从这个项目开始,我就开源了所有代码,并免费帮助了数十甚至上百位朋友解决了他们的问题。现在我不得不对未来的帮助收费,每个功能实现从49美元起;每个功能咨询19美元起。所有改进都将开源以帮助其他人。详情请联系329055754@qq.com。谢谢!
这是用Java实现的
支持的样式:
- 粗体
- 斜体
- 下划线
- 删除线
- 数字列表
- 项目符号列表
- 左对齐
- 居中对齐
- 右对齐
- 插入图片
- 背景颜色
- 超链接
- @
- 引用
- 前景色
- 表情图标
- 上标
- 下标
- 字体大小
- 视频
- 网络图片
- 分隔线
- 所有样式支持保存为HTML文件
- 从HTML加载后继续编辑或显示 - 0.1.0版本新功能
0.2.0版本发布:迁移到AndroidX
- 更新Glide至4.11.0
- 最后一个非AndroidX版本是0.1.10
0.1.8版本发布:增加了在一个页面中支持多个ARE实例的功能
0.1.7版本发布:更新Glide至4.9.0
0.1.6版本
- 维护性发布,包括:
- 在ARE_Toolbar_Default中添加了前景色样式
- 在ARE_Toolbar_Default中添加了背景色样式
- 修复bug
下一版本计划:
- 撤销
- 重做
如果你有任何想在下一版本中添加的功能,请告诉我,谢谢。
ARE工作原理
-
两种使用模式
-
AREditor。编辑框(输入区域)和工具栏(样式工具)在同一个组件中。即:如果你在布局XML中包含
<com.chinalwb.are.AREditor .../>
,你将在活动中看到输入区域和样式工具(粗体/斜体/对齐/项目符号列表等)。 -
AREditText + IARE_Toolbar(这是一个接口)。编辑框(输入区域)和工具栏(样式工具)是独立的组件。即:你可以选择是否显示工具栏,以及将其放置在AREditText的下方或底部、左侧或右侧,或任何其他对齐方式,因为工具栏本身是一个
HorizontalScrollView
(默认实现是ARE_ToolbarDefault
),同时你还可以决定向工具栏添加哪些工具项。
-
-
这两种模式的内部结构:
-
AREditor:它扩展自
RelativeLayout
,包含两个子组件:AREditText
和ARE_Toolbar
。-
AREditText
扩展自AppCompatEditText
,在其TextWatcher
的afterTextChanged
方法中,它调用IARE_Style
的applyStyle
。 -
ARE_Toolbar
扩展自LinearLayout
,内部的工具项是IARE_Style
实例,每个实例包含一个在工具栏中显示的ImageView
。
-
-
AREditText + IARE_Toolbar。
-
关于
AREditText
无需重复 -
IARE_Toolbar
是一个接口,定义了工具栏需要实现的功能。如:addToolbarItem
、getToolItems
等。在are
中,有一个默认实现:ARE_ToolbarDefault
,它扩展自HorizontalScrollView
,并实现了IARE_Toolbar
接口中的所有方法。 -
ARE_ToolbarDefault
只是一个工具栏,即:工具项容器,真正执行工作的组件是IARE_ToolItem
,这是另一个定义工具项行为的接口,如:getView
返回在工具栏中显示的视图,getStyle
返回一个IARE_Style
来响应文本变化。目前有19个工具项实现:-
ARE_ToolItem_Abstract ARE_ToolItem_AlignmentCenter ARE_ToolItem_AlignmentLeft ARE_ToolItem_AlignmentRight ARE_ToolItem_At ARE_ToolItem_Bold ARE_ToolItem_Hr ARE_ToolItem_Image ARE_ToolItem_Italic ARE_ToolItem_Link ARE_ToolItem_ListBullet ARE_ToolItem_ListNumber ARE_ToolItem_Quote ARE_ToolItem_Strikethrough ARE_ToolItem_Subscript ARE_ToolItem_Superscript ARE_ToolItem_Underline ARE_ToolItem_UpdaterDefault ARE_ToolItem_Video
-
上述工具项可以通过调用
addToolbarItem(IARE_ToolItem toolItem)
添加到工具栏
-
-
-
-
如果你想添加自己的工具项,只需要实现你的
IARE_ToolItem
接口即可。例如,如果你想添加一个更改字体的工具项,那么你可以定义ARE_ToolItem_FontFamily并实现IARE_ToolItem
中的方法。你可以参考上述任何一个工具项作为参考。 -
特别是如果你想添加一个新功能,比如
@
或者##
,可以查看ARE_ToolItem_At.java,这是一个没有工具栏图标的工具项示例。
- 所有样式都基于Android Spans
集成
在你的app模块的gradle.build文件中,在dependencies中添加以下内容:
implementation 'com.github.bumptech.glide:glide:4.9.0'
implementation 'com.github.chinalwb:are:0.1.7'
或者,由于are
还不够稳定,无法处理不同业务场景中的各种问题,我建议直接将are
导入到你的项目中,并将其作为本地模块依赖。
implementation project(':are')
自定义和示例
布局XML中AREditor
的文档
名称 | 格式 | 描述 |
---|---|---|
expandMode | 枚举 | FULL(默认:全屏编辑器)/ MIN(最小高度编辑器,maxLines = 3) |
hideToolbar | 布尔值 | 是否隐藏工具栏,默认显示工具栏。当你使用MIN 展开模式时,可能需要将其设置为true,@ 功能仍然可用,但其他功能将无法使用,因为工具栏上的那些样式已被隐藏。 |
toolbarAlignment | 枚举 | BOTTOM(默认:在AREditor底部)/ TOP(在AREditor顶部) |
Java中AREditor
的API
类 | 方法 | 参数 | 描述 |
---|---|---|---|
AREditor | setExpandMode | AREditor.ExpandMode | 设置编辑区域模式。可能的值有:ExpandMode.FULL(默认)/ ExpandMode.MIN |
AREditor | setHideToolbar | boolean | 设置为true隐藏工具栏;设置为false显示工具栏 |
AREditor | setToolbarAlignment | AREditor.ToolbarAlignment | 设置工具栏位置。可能的值有:ToolbarAlignment.BOTTOM (默认)/ ToolbarAlignment.TOP |
AREditor
示例
XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:are="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_dark"
>
<TextView
android:id="@+id/xView"
android:layout_above="@+id/areditor"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="1dp"
android:background="@color/colorAccent"
android:gravity="center"
android:textSize="50sp"
android:text="你的ListView可能在这里"
/>
<com.chinalwb.are.AREditor
android:id="@+id/areditor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@android:color/holo_green_dark"
are:expandMode="MIN"
are:hideToolbar="true"
are:toolbarAlignment="TOP" />
</RelativeLayout>
Java:
AREditor arEditor = this.findViewById(R.id.areditor);
arEditor.setExpandMode(AREditor.ExpandMode.FULL);
arEditor.setHideToolbar(false);
arEditor.setToolbarAlignment(AREditor.ToolbarAlignment.BOTTOM);
==========
AREditText
本身是AppCompatEditText
的子类,所以适用于AppCompatEditText
的任何东西也适用于AREditText
。
==========
ARE_ToolbarDefault
文档
它继承自HorizontalScrollView
,所以适用于HorizontalScrollView
的任何东西也适用于ARE_ToolbarDefault
。
IARE_Toolbar
的API
类 | 方法 | 参数 | 描述 |
---|---|---|---|
IARE_Toolbar | addToolbarItem | IARE_ToolItem | 向工具栏添加工具项 |
IARE_Toolbar | getToolItems | -无- | 返回工具栏中的所有工具项 |
IARE_Toolbar | setEditText | AREditText | 将AREditText与工具栏绑定 |
IARE_Toolbar | getEditText | -无- | 返回绑定的AREditText |
IARE_Toolbar | onActivityResult | requestCode(int) resultCode(int) data(Intent) | 对于一些样式,如插入图片、视频或@功能,你需要打开一个新的Activity,并需要通过onActivityResult处理数据,在这个方法中你可以分发到特定的样式。 |
IARE_ToolItem
的API
类 | 方法 | 参数 | 描述 |
---|---|---|---|
IARE_ToolItem | getStyle | -无- | 每个工具项都是一个样式,一个样式与特定的span结合。 |
IARE_ToolItem | getView | Context | 每个工具项都有一个视图。如果context为null,返回生成的视图。 |
IARE_ToolItem | onSelectionChanged | int selStart, int selEnd | 选择改变回调。更新工具项选中状态 |
IARE_ToolItem | getToolbar | -无- | 返回此工具项的工具栏。 |
IARE_ToolItem | setToolbar | IARE_Toolbar | 为此工具项设置工具栏。 |
IARE_ToolItem | getToolItemUpdater | -无- | 获取工具项更新器实例,在样式被选中和取消选中时会被调用。 |
IARE_ToolItem | setToolItemUpdater | IARE_ToolItem_Updater | 设置工具项更新器。 |
IARE_ToolItem | onActivityResult | requestCode(int) resultCode(int) data(Intent) | 处理从IARE_Toolbar分发的事件 |
示例:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:are="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_dark">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/bottombar"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:background="@android:color/white">
<com.chinalwb.are.AREditText
android:id="@+id/arEditText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="top|left"
android:hint="在此输入文本"
android:textSize="50sp" />
</ScrollView>
<LinearLayout
android:id="@+id/bottombar"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:orientation="horizontal"
android:weightSum="1000">
<com.chinalwb.are.styles.toolbar.ARE_ToolbarDefault
android:id="@+id/areToolbar"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="900"
android:background="@color/colorPrimary"
android:gravity="center_vertical" />
<View
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="5"
android:background="@color/colorPrimaryDark" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="95"
android:background="@color/colorAccent"
android:gravity="center">
<ImageView
android:id="@+id/arrow"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/arrow_right" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>
Java示例代码:设置自定义工具栏
public class ARE_DefaultToolbarActivity extends AppCompatActivity {
private IARE_Toolbar mToolbar;
private AREditText mEditText;
private boolean scrollerAtEnd;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_are__default_toolbar);
initToolbar();
}
private void initToolbar() {
mToolbar = this.findViewById(R.id.areToolbar);
IARE_ToolItem bold = new ARE_ToolItem_Bold();
IARE_ToolItem italic = new ARE_ToolItem_Italic();
IARE_ToolItem underline = new ARE_ToolItem_Underline();
IARE_ToolItem strikethrough = new ARE_ToolItem_Strikethrough();
IARE_ToolItem quote = new ARE_ToolItem_Quote();
IARE_ToolItem listNumber = new ARE_ToolItem_ListNumber();
IARE_ToolItem listBullet = new ARE_ToolItem_ListBullet();
IARE_ToolItem hr = new ARE_ToolItem_Hr();
IARE_ToolItem link = new ARE_ToolItem_Link();
IARE_ToolItem subscript = new ARE_ToolItem_Subscript();
IARE_ToolItem superscript = new ARE_ToolItem_Superscript();
IARE_ToolItem left = new ARE_ToolItem_AlignmentLeft();
IARE_ToolItem center = new ARE_ToolItem_AlignmentCenter();
IARE_ToolItem right = new ARE_ToolItem_AlignmentRight();
IARE_ToolItem image = new ARE_ToolItem_Image();
IARE_ToolItem video = new ARE_ToolItem_Video();
IARE_ToolItem at = new ARE_ToolItem_At();
mToolbar.addToolbarItem(bold);
mToolbar.addToolbarItem(italic);
mToolbar.addToolbarItem(underline);
mToolbar.addToolbarItem(strikethrough);
mToolbar.addToolbarItem(quote);
mToolbar.addToolbarItem(listNumber);
mToolbar.addToolbarItem(listBullet);
mToolbar.addToolbarItem(hr);
mToolbar.addToolbarItem(link);
mToolbar.addToolbarItem(subscript);
mToolbar.addToolbarItem(superscript);
mToolbar.addToolbarItem(left);
mToolbar.addToolbarItem(center);
mToolbar.addToolbarItem(right);
mToolbar.addToolbarItem(image);
mToolbar.addToolbarItem(video);
mToolbar.addToolbarItem(at);
mEditText = this.findViewById(R.id.arEditText);
mEditText.setToolbar(mToolbar);
setHtml();
initToolbarArrow();
}
private void setHtml() {
String html = "<p style=\"text-align: center;\"><strong>0.1.2版本新特性</strong></p>\n" +
"<p style=\"text-align: center;\"> </p>\n" +
"<p style=\"text-align: left;\"><span style=\"color: #3366ff;\">在此版本中,您可以使用ARE的新用法。</span></p>\n" +
"<p style=\"text-align: left;\"> </p>\n" +
"<p style=\"text-align: left;\"><span style=\"color: #3366ff;\">AREditText + ARE_Toolbar,现在您可以控制输入区域的位置、工具栏的放置位置以及工具栏中要包含的工具项。</span></p>\n" +
"<p style=\"text-align: left;\"> </p>\n" +
"<p style=\"text-align: left;\"><span style=\"color: #3366ff;\">您不仅可以定义工具栏(及其样式),还可以将自定义的ARE_ToolItem及其样式添加到ARE中。</span></p>\n" +
"<p style=\"text-align: left;\"> </p>\n" +
"<p style=\"text-align: left;\"><span style=\"color: #ff00ff;\"><em><strong>何不现在就试试看?</strong></em></span></p>";
mEditText.fromHtml(html);
}
private void initToolbarArrow() {
final ImageView imageView = this.findViewById(R.id.arrow);
if (this.mToolbar instanceof ARE_ToolbarDefault) {
((ARE_ToolbarDefault) mToolbar).getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
int scrollX = ((ARE_ToolbarDefault) mToolbar).getScrollX();
int scrollWidth = ((ARE_ToolbarDefault) mToolbar).getWidth();
int fullWidth = ((ARE_ToolbarDefault) mToolbar).getChildAt(0).getWidth();
如果 (滚动X + 滚动宽度 < 总宽度) {
图片视图.设置图片资源(R.drawable.右箭头);
滚动到末端 = false;
} else {
图片视图.设置图片资源(R.drawable.左箭头);
滚动到末端 = true;
}
}
});
}
图片视图.设置点击监听器(new 视图.点击监听器() {
@Override
public void 点击(视图 view) {
if (滚动到末端) {
((ARE_默认工具栏) 工具栏).平滑滚动(-整数.最大值, 0);
滚动到末端 = false;
} else {
int 水平滚动宽度 = ((ARE_默认工具栏) 工具栏).getChildAt(0).getWidth();
((ARE_默认工具栏) 工具栏).平滑滚动(水平滚动宽度, 0);
滚动到末端 = true;
}
}
});
}
@Override
public boolean 创建选项菜单(菜单 menu) {
获取菜单填充器().填充(R.menu.main, menu);
return true;
}
@Override
public boolean 选项菜单项被选中(菜单项 item) {
int 菜单ID = item.getItemId();
if (菜单ID == com.chinalwb.are.R.id.action_save) {
String html = this.编辑文本.获取Html();
演示工具.保存Html(this, html);
return true;
}
if (菜单ID == R.id.action_show_tv) {
String html = this.编辑文本.获取Html();
Intent intent = new Intent(this, 文本视图活动.class);
intent.putExtra(HTML_文本, html);
启动活动(intent);
return true;
}
return super.选项菜单项被选中(item);
}
@Override
protected void 活动结果(int 请求码, int 结果码, Intent 数据) {
工具栏.活动结果(请求码, 结果码, 数据);
}
}
AREditor 的 API
类 | 方法 | 参数 | 描述 |
---|---|---|---|
AREditor | 从Html | 字符串 | 将html加载到AREditor |
AREditor | 获取Html | 无 | 返回AREditor中当前内容的HTML源码 |
AREditor | 获取ARE | 无 | 返回此AREditor中的AREditText实例 |
AREditText 的 API
类 | 方法 | 参数 | 描述 |
---|---|---|---|
AREditText | 从Html | 无 | 将html加载到AREditor |
AREditText | 获取Html | 无 | 返回AREditor中当前内容的HTML源码 |
AREditText | 设置工具栏 | IARE_工具栏 | 设置IARE_工具栏实例(仅在作为分离组件工作并与自定义工具栏一起工作时需要) |
更多示例可以在这里找到。
可用功能演示:
[图片]
[动图]
进行中的项目:
- 音频
- 字体系列
- 右缩进
- 左缩进
- 将编辑保存到本地SQLite
- 笔记列表
- 标题 - 延期,可以通过字体大小和居中样式完成
你可以在这里下载APK:
已知问题:
- 背景颜色 - 将光标放在BackgroundColorSpan范围内时不可见
感谢 @Yasujizr 为ARE提供了logo。@Yasujizr 我希望现在能得到一个新logo :) 你能帮我吗?
如果你发现我的工作对你有帮助或者你开始使用我的代码,你不需要给我买咖啡,只是你能给我发一个"✨"吗?你的*鼓励我开源更多功能,谢谢你的支持。 如果你需要任何定制或有任何建议,可以联系我,邮箱是329055754@qq.com。