如何实现一个 Android 端的富js实现文本编辑器器

2051人阅读
Android(8)
最近看到了简书App中的编辑器可以实现字体的加粗,斜体,删除线等多种样式,而且可以插入图片,链接,分割线。支持字符串数据提交服务器,然后在TextView中直接展示。
如果我们没有了解其中原理之前,感觉还是挺高大上的。然后我就打算仿照他写一个类似的给大家分享。
开始我在网上找了一些类似的Demo,发现实现的关键原理是:通过WebView加载Html标签实现效果展示,然后最终获取全部的Html语句提交服务器,然后我们在请求服务器获取Html标签字符串,直接TextView展示。
不过在网上找了很多都最终达不到简书的那种效果,然后我就对部分进行了重写和添加,最终实现了和简书几乎一样的效果。
第一步:自定义WebView并初始Html化标签字符串
private static final String SETUP_HTML = &file:///android_asset/editor.html&;
private static final String CALLBACK_SCHEME = &re-callback://&;
private static final String STATE_SCHEME = &re-state://&;
private boolean isReady =
private String mC
private OnTextChangeListener mTextChangeL
private OnDecorationStateListener mDecorationStateL
private AfterInitialLoadListener mLoadL
private OnScrollChangedCallback mOnScrollChangedC
public RichEditor(Context context) {
this(context, null);
public RichEditor(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.webViewStyle);
@SuppressLint(&SetJavaScriptEnabled&)
public RichEditor(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setVerticalScrollBarEnabled(false);
setHorizontalScrollBarEnabled(false);
getSettings().setJavaScriptEnabled(true);
setWebChromeClient(new WebChromeClient());
setWebViewClient(createWebviewClient());
loadUrl(SETUP_HTML);
applyAttributes(context, attrs);
loadUrl(SETUP_HTML);
我们可以看到加载了一个本地的Html文件
&!DOCTYPE html&
&meta name=&viewport& content=&user-scalable=no&&
&meta http-equiv=&Content-Type& content=&text/ charset=UTF-8&&
&link rel=&stylesheet& type=&text/css& href=&normalize.css&&
&link rel=&stylesheet& type=&text/css& href=&style.css&&
&div id=&editor& contentEditable=&true&&&/div&
&script type=&text/javascript& src=&rich_editor.js&&&/script&
&link rel=&stylesheet& type=&text/css& href=&normalize.css&&
&link rel=&stylesheet& type=&text/css& href=&style.css&&&pre name=&code& class=&html& style=&font-size: 13.3333&&
script type=&text/javascript& src=&rich_editor.js&&&/script&
在Html文件中连接了两个css文件和一个js文件
* Copyright (C) 2015 Wasabeef
* Licensed under the Apache License, Version 2.0 (the &License&);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an &AS IS& BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
@charset &UTF-8&;
height: 100%;
table-layout:
width: 100%;
min-height:100%;
display: table-
-webkit-user-select: auto !
-webkit-user-modify: read-write !
outline: 0
background-repeat: no-
background-position:
background-size:
blockquote{
background-color:
border-left: 4px solid #999999;
font-size: 15
font-weight: 100;
padding: 10px 15
margin-left: 0
margin-right : 0
#editor[placeholder]:empty:not(:focus):before {
content: attr(placeholder);
opacity: .5;
其余两个代码较多就不进行展示了,末尾有下载地址
开始编辑富文本
1,控件使用
&span style=&white-space:pre&& &/span&&com.niuduz.richeditor_ding.richeditor.RichEditor
android:id=&@+id/editor&
android:layout_width=&match_parent&
android:layout_height=&@dimen/dimen_300dip&
android:layout_marginLeft=&@dimen/dimen_5dip&
android:layout_marginRight=&@dimen/dimen_5dip&
android:gravity=&top|left&
android:paddingTop=&@dimen/dimen_10dip& /&
2,添加按钮布局
&RelativeLayout
android:id=&@+id/rl_layout_editor&
android:layout_width=&match_parent&
android:layout_height=&wrap_content&
android:visibility=&invisible&&
android:layout_width=&match_parent&
android:layout_height=&@dimen/dimen_1dip&
android:layout_above=&@+id/ll_layout_editor&
android:background=&@color/split_line_color& /&
&LinearLayout
android:id=&@+id/ll_layout_editor&
android:layout_width=&match_parent&
android:layout_height=&@dimen/dimen_36dip&
android:layout_alignParentBottom=&true&
android:background=&@color/white&
android:orientation=&horizontal&&
&ImageButton
android:id=&@+id/action_undo&
android:layout_width=&0dp&
android:layout_height=&match_parent&
android:layout_weight=&1&
android:background=&@null&
android:contentDescription=&@null&
android:src=&@mipmap/undo& /&
&ImageButton
android:id=&@+id/action_redo&
android:layout_width=&0dp&
android:layout_height=&match_parent&
android:layout_weight=&1&
android:background=&@null&
android:contentDescription=&@null&
android:src=&@mipmap/redo& /&
&ImageButton
android:id=&@+id/action_font&
android:layout_width=&0dp&
android:layout_height=&match_parent&
android:layout_weight=&1&
android:background=&@null&
android:contentDescription=&@null&
android:src=&@mipmap/font& /&
&ImageButton
android:id=&@+id/action_add&
android:layout_width=&0dp&
android:layout_height=&match_parent&
android:layout_weight=&1&
android:background=&@null&
android:contentDescription=&@null&
android:src=&@mipmap/add& /&
&/LinearLayout&
&LinearLayout
android:id=&@+id/ll_layout_font&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:layout_above=&@+id/ll_layout_editor&
android:layout_alignParentEnd=&true&
android:layout_marginBottom=&-18dp&
android:layout_marginRight=&-5dp&
android:background=&@drawable/richfont_bg&
android:gravity=&center&
android:orientation=&horizontal&
android:visibility=&gone&&
&ImageButton
android:id=&@+id/action_bold&
android:layout_width=&@dimen/dimen_36dip&
android:layout_height=&@dimen/dimen_36dip&
android:background=&@null&
android:contentDescription=&@null&
android:src=&@mipmap/bold_d& /&
&ImageButton
android:id=&@+id/action_italic&
android:layout_width=&@dimen/dimen_36dip&
android:layout_height=&@dimen/dimen_36dip&
android:background=&@null&
android:contentDescription=&@null&
android:src=&@mipmap/italic_d& /&
&ImageButton
android:id=&@+id/action_strikethrough&
android:layout_width=&@dimen/dimen_36dip&
android:layout_height=&@dimen/dimen_36dip&
android:background=&@null&
android:contentDescription=&@null&
android:src=&@mipmap/strikethrough_d& /&
&ImageButton
android:id=&@+id/action_blockquote&
android:layout_width=&@dimen/dimen_36dip&
android:layout_height=&@dimen/dimen_36dip&
android:background=&@null&
android:contentDescription=&@null&
android:src=&@mipmap/blockquote_d& /&
&ImageButton
android:id=&@+id/action_heading1&
android:layout_width=&@dimen/dimen_36dip&
android:layout_height=&@dimen/dimen_36dip&
android:background=&@null&
android:contentDescription=&@null&
android:src=&@mipmap/h1_d& /&
&ImageButton
android:id=&@+id/action_heading2&
android:layout_width=&@dimen/dimen_36dip&
android:layout_height=&@dimen/dimen_36dip&
android:background=&@null&
android:contentDescription=&@null&
android:src=&@mipmap/h2_d& /&
&ImageButton
android:id=&@+id/action_heading3&
android:layout_width=&@dimen/dimen_36dip&
android:layout_height=&@dimen/dimen_36dip&
android:background=&@null&
android:contentDescription=&@null&
android:src=&@mipmap/h3_d& /&
&ImageButton
android:id=&@+id/action_heading4&
android:layout_width=&@dimen/dimen_36dip&
android:layout_height=&@dimen/dimen_36dip&
android:background=&@null&
android:contentDescription=&@null&
android:src=&@mipmap/h4_d& /&
&/LinearLayout&
&LinearLayout
android:id=&@+id/ll_layout_add&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:layout_above=&@+id/ll_layout_editor&
android:layout_alignParentEnd=&true&
android:layout_marginBottom=&-18dp&
android:layout_marginRight=&@dimen/dimen_12dip&
android:background=&@drawable/richadd_bg&
android:gravity=&center&
android:orientation=&horizontal&
android:paddingLeft=&@dimen/dimen_20dip&
android:paddingRight=&@dimen/dimen_20dip&
android:visibility=&gone&&
&ImageButton
android:id=&@+id/action_image&
android:layout_width=&wrap_content&
android:layout_height=&match_parent&
android:background=&@null&
android:contentDescription=&@null&
android:paddingRight=&@dimen/dimen_10dip&
android:src=&@mipmap/insert_image& /&
&ImageButton
android:id=&@+id/action_link&
android:layout_width=&wrap_content&
android:layout_height=&match_parent&
android:background=&@null&
android:contentDescription=&@null&
android:paddingLeft=&@dimen/dimen_10dip&
android:paddingRight=&@dimen/dimen_10dip&
android:src=&@mipmap/insert_link& /&
&ImageButton
android:id=&@+id/action_split&
android:layout_width=&wrap_content&
android:layout_height=&match_parent&
android:background=&@null&
android:contentDescription=&@null&
android:paddingLeft=&@dimen/dimen_10dip&
android:src=&@mipmap/insert_split& /&
&/LinearLayout&
&/RelativeLayout&
· 3.注册RichEditor和各个按钮相关事件
action_add.setOnClickListener(this);
action_font.setOnClickListener(this);
action_redo.setOnClickListener(this);
action_undo.setOnClickListener(this);
ib_Bold.setOnClickListener(this);
ib_Italic.setOnClickListener(this);
ib_StrikeThough.setOnClickListener(this);
ib_BlockQuote.setOnClickListener(this);
ib_H1.setOnClickListener(this);
ib_H2.setOnClickListener(this);
ib_H3.setOnClickListener(this);
ib_H4.setOnClickListener(this);
mEditor.setOnFocusChangeListener(new View.OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
rl_layout_editor.setVisibility(View.VISIBLE);
clickableType = 1;
imm.hideSoftInputFromWindow(mEditor.getWindowToken(), 0); //强制隐藏键盘
rl_layout_editor.setVisibility(View.INVISIBLE);
*获取点击出文本的标签类型
mEditor.setOnDecorationChangeListener(new RichEditor.OnDecorationStateListener() {
public void onStateChangeListener(String text, List&RichEditor.Type& types) {
if (types.contains(RichEditor.Type.BOLD)) {
ib_Bold.setImageResource(R.mipmap.bold_l);
ib_Bold.setImageResource(R.mipmap.bold_d);
if (types.contains(RichEditor.Type.ITALIC)) {
ib_Italic.setImageResource(R.mipmap.italic_l);
isItalic =
ib_Italic.setImageResource(R.mipmap.italic_d);
isItalic =
if (types.contains(RichEditor.Type.STRIKETHROUGH)) {
ib_StrikeThough.setImageResource(R.mipmap.strikethrough_l);
isStrikeThrough =
ib_StrikeThough.setImageResource(R.mipmap.strikethrough_d);
isStrikeThrough =
if (types.contains(RichEditor.Type.BLOCKQUOTE)) {
ib_BlockQuote.setImageResource(R.mipmap.blockquote_l);
ib_H1.setImageResource(R.mipmap.h1_d);
ib_H2.setImageResource(R.mipmap.h2_d);
ib_H3.setImageResource(R.mipmap.h3_d);
ib_H4.setImageResource(R.mipmap.h4_d);
ib_BlockQuote.setImageResource(R.mipmap.blockquote_d);
if (types.contains(RichEditor.Type.H1)) {
ib_BlockQuote.setImageResource(R.mipmap.blockquote_d);
ib_H1.setImageResource(R.mipmap.h1_l);
ib_H2.setImageResource(R.mipmap.h2_d);
ib_H3.setImageResource(R.mipmap.h3_d);
ib_H4.setImageResource(R.mipmap.h4_d);
ib_H1.setImageResource(R.mipmap.h1_d);
if (types.contains(RichEditor.Type.H2)) {
ib_BlockQuote.setImageResource(R.mipmap.blockquote_d);
ib_H1.setImageResource(R.mipmap.h1_d);
ib_H2.setImageResource(R.mipmap.h2_l);
ib_H3.setImageResource(R.mipmap.h3_d);
ib_H4.setImageResource(R.mipmap.h4_d);
ib_H2.setImageResource(R.mipmap.h2_d);
if (types.contains(RichEditor.Type.H3)) {
ib_BlockQuote.setImageResource(R.mipmap.blockquote_d);
ib_H1.setImageResource(R.mipmap.h1_d);
ib_H2.setImageResource(R.mipmap.h2_d);
ib_H3.setImageResource(R.mipmap.h3_l);
ib_H4.setImageResource(R.mipmap.h4_d);
ib_H4.setImageResource(R.mipmap.h3_d);
if (types.contains(RichEditor.Type.H4)) {
ib_BlockQuote.setImageResource(R.mipmap.blockquote_d);
ib_H1.setImageResource(R.mipmap.h1_d);
ib_H2.setImageResource(R.mipmap.h2_d);
ib_H3.setImageResource(R.mipmap.h3_d);
ib_H4.setImageResource(R.mipmap.h4_l);
ib_H4.setImageResource(R.mipmap.h4_d);
然后在事件监听中,进行相关处理,这其中通常是对其他按钮作用的效果的添加和移除。这个是富文本中处理最麻烦的
因为WebView对标签的包裹并非统一实现了,基本原则:把出现标签的效果按钮就变亮;把没有出现标签效果的按钮变灰。
最后效果:
1,在模拟器上撤销和返回两个按钮好像有问题,在真机上完全没事!
2,在各个事件监听逻辑中,为了添加和消除其他按钮的影响时,产生了大量的重复代码,虽然大致相同,但还是存在区别,所以感觉抽取也不是,不抽取也不是。这方便有待优化,也请在有好的处理方法的话多多指出!
源码下载链接:
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:11850次
排名:千里之外
原创:10篇
评论:20条
(1)(2)(4)(5)(1)9个牛币请下载代码后再发表评论//simditor/simditor/simditor/simditor/simditor/.classpath/simditor/simditor/.mymetadata/simditor/simditor/.project/simditor/simditor/.settings/simditor/simditor/.settings/.jsdtscope/simditor/simditor/.settings/com.genuitec.eclipse.j2eedt.core.prefs/simditor/simditor/.settings/org.eclipse.core.resources.prefs/simditor/simditor/.settings/org.eclipse.jdt.core.prefs/simditor/simditor/.settings/org.eclipse.wst.jsdt.ui.superType.container/simditor/simditor/.settings/org.eclipse.wst.jsdt.ui.superType.name/simditor/simditor/src/simditor/simditor/src/com/simditor/simditor/src/com/home/simditor/simditor/src/com/home/util精精精精原精精原精原原精原精原原精最热搜索分享话题编程语言基础Web开发数据库开发客户端开发脚本工具游戏开发服务器软硬件开源组件类库相关分享原精最近下载暂无贡献等级暂无贡献等级暂无贡献等级暂无贡献等级暂无贡献等级暂无贡献等级&LV1暂无贡献等级最近浏览暂无贡献等级暂无贡献等级暂无贡献等级暂无贡献等级暂无贡献等级暂无贡献等级暂无贡献等级暂无贡献等级扫描二维码关注最代码为好友"/>扫描二维码关注最代码为好友富文本编辑器_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!& & 这里简单的聊一下如何实现一个简单的富文本编辑器。见以前的文章动手做一个在线富文本编辑器。& & 1.富文本编辑器的基础& & document对象有一个designMode属性,你可以使用这个属性来让当前的document进入可编辑模式。& &……
声明:该文章系网友上传分享,此内容仅代表网友个人经验或观点,不代表本网站立场和观点;若未进行原创声明,则表明该文章系转载自互联网;若该文章内容涉嫌侵权,请及时向
论文写作技巧
上一篇:下一篇:
相关经验教程主题信息(必填)
主题描述(最多限制在50个字符)
申请人信息(必填)
申请信息已提交审核,请注意查收邮件,我们会尽快给您反馈。
如有疑问,请联系
我的微信公众号——程序视界,聚焦程序员的职业选择、适应与发展。
生命不息,折腾不止
每个人都在成神的路上,只不过有的人在走,而有的人在跑。}

我要回帖

更多关于 html富文本编辑器实现 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信