0


【Android】基于webView打造富文本编辑器(H5)

目录


前言

HTML5是构建Web内容的一种语言描述方式。HTML5是Web中核心语言HTML的规范,用户使用任何手段进行网页浏览时看到的内容原本都是HTML格式的,在浏览器中通过一些技术处理将其转换成为了可识别的信息。

而WebView 是一种嵌入式浏览器,原生APP应用可以用它来展示网络内容。其功能强大,除了具有一般View的属性和设置外,还可以对url请求、页面加载、渲染、页面交互进行强大的处理.。
在这里插入图片描述
所以,因为H5的跨平台和成本低的优势,越来越多的项目都使用了Android原生控件与WebView进行混合开发,用WebView加载html界面,实现本地安卓代码与HTML+CSS+JS的交互。

webView的基本使用可以参考这篇文章:https://blog.csdn.net/carson_ho/article/details/52693322

本文将利用webView实现一个富文本编辑器,它是一种可内嵌于浏览器,所见即所得的文本编辑器,其实质就是html代码,可改变文本样式、文间插入图片或视频等。我为了实现这一功能借鉴很多项目(主要是MRichEditor和RichEditTextCopyToutiao,都是基于 richeditor-android),修复了一些Bug,最终效果还行。借鉴的第三方库可移步我之前写的文章:常用的第三方开源库汇总


一、实现效果

在这里插入图片描述

在这里插入图片描述

二、具体实现

富文本编辑器主要功能就是文字大小、颜色、加粗、斜体、下划线、删除线、缩进、居中或靠左靠右,无序或有序列表,插入链接、图片、视频等。原有项目会存在一些导入html标签和输入框光标的bug,解决这些需要一点HTML+CSS+JS的知识。

1. 导入网页资源

在项目根目录下新建assets文件夹
在这里插入图片描述
导入资源:对于熟悉网页端的开发人员来说,修改这些并不难。
在这里插入图片描述

2. 页面设计

编辑器的一个按钮即对应着js中的一个函数,通过RichEditor自定义 view去调用js函数, 它继承自 Webview ,加载html文件,枚举类型Type定义了支持的排版格式。

  1. publicclassRichEditorextendsWebView{privatestaticfinalStringSETUP_HTML="file:///android_asset/editor.html";publicenumType{BOLD,ITALIC,SUBSCRIPT,SUPERSCRIPT,STRIKETHROUGH,UNDERLINE,H1,H2,H3,H4,H5,H6,ORDEREDLIST,UNORDEREDLIST,JUSTIFYCENTER,JUSTIFYFULL,JUSTIFYLEFT,JUSTIFYRIGHT}@SuppressLint("SetJavaScriptEnabled")publicRichEditor(Context context,AttributeSet attrs,int defStyleAttr){super(context, attrs, defStyleAttr);setVerticalScrollBarEnabled(false);setHorizontalScrollBarEnabled(false);getSettings().setJavaScriptEnabled(true);setWebChromeClient(newWebChromeClient());setWebViewClient(createWebviewClient());loadUrl(SETUP_HTML);applyAttributes(context, attrs);}}

其中​editor.html​即编辑器所展示的主页面

  1. <!DOCTYPEhtml><html><head><metaname="viewport"content="user-scalable=no"><metahttp-equiv="Content-Type"content="text/html; charset=UTF-8"><linkrel="stylesheet"type="text/css"href="normalize.css"><linkrel="stylesheet"type="text/css"href="style.css"></head><body><divid="editor"contenteditable="true"></div><scripttype="text/javascript"src="rich_editor.js"></script></body></html>

RichEditor控件中的一个功能就对应着​rich_editor.js​中的一个函数,例如加粗效果:

RichEditor:

  1. publicvoidsetBold(){exec("javascript:RE.setBold();");}

​rich_editor.js:

  1. RE.setBold=function(){
  2. document.execCommand('bold',false,null);RE.enabledEditingItems()}

最后,RichEditor在布局中使用:

  1. <com.text.richeditor.editor.RichEditorandroid:id="@+id/rich_Editor"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:overScrollMode="never"/>

主要样式如下:
在这里插入图片描述
除了底部的功能栏可以使用,最左边还有一个排版按钮,这里主要是为了不方便弹出底部时,可以选择使用弹窗弹出功能栏,功能是一致的,可以选择性阉割。使用某一功能时会有颜色高亮提示。

3. 功能调用

首先要在activity中初始化控件,实现setOnDecorationChangeListener接口,监听点击某个功能按钮时显示对应高亮,让用户看到现在正在所使用的排版格式。

  1. //输入框显示字体的大小
  2. rich_Editor.setEditorFontSize(16)//输入框显示字体的颜色
  3. rich_Editor.setEditorFontColor(ContextCompat.getColor(this,R.color.deepGrey))//输入框背景设置
  4. rich_Editor.setEditorBackgroundColor(Color.WHITE)//输入框文本padding
  5. rich_Editor.setPadding(10,10,10,10)//输入提示文本
  6. rich_Editor.setPlaceholder("点击输入正文!~")
  7. rich_Editor.setOnDecorationChangeListener{ _, types ->val flagArr = ArrayList<String>()for(i in types.indices){
  8. flagArr.add(types[i].name)}if(flagArr.contains("BOLD")){
  9. button_bold.setColorFilter(
  10. ContextCompat.getColor(this@RichTextActivity,
  11. R.color.colorPrimary
  12. ))
  13. mEditorMenuFragment.updateActionStates(ActionType.BOLD,true)}else{
  14. button_bold.setColorFilter(
  15. ContextCompat.getColor(this@RichTextActivity,
  16. R.color.tintColor
  17. ))
  18. mEditorMenuFragment.updateActionStates(ActionType.BOLD,false)}if(flagArr.contains("ITALIC")){
  19. button_italic.setColorFilter(
  20. ContextCompat.getColor(this@RichTextActivity,
  21. R.color.colorPrimary
  22. ))
  23. mEditorMenuFragment.updateActionStates(ActionType.ITALIC,true)}else{
  24. button_italic.setColorFilter(
  25. ContextCompat.getColor(this@RichTextActivity,
  26. R.color.tintColor
  27. ))
  28. mEditorMenuFragment.updateActionStates(ActionType.ITALIC,false)}if(flagArr.contains("STRIKETHROUGH")){
  29. button_strikethrough.setColorFilter(
  30. ContextCompat.getColor(this@RichTextActivity,
  31. R.color.colorPrimary
  32. ))
  33. mEditorMenuFragment.updateActionStates(ActionType.STRIKETHROUGH,true)}else{
  34. button_strikethrough.setColorFilter(
  35. ContextCompat.getColor(this@RichTextActivity,
  36. R.color.tintColor
  37. ))
  38. mEditorMenuFragment.updateActionStates(ActionType.STRIKETHROUGH,false)}if(flagArr.contains("JUSTIFYCENTER")){
  39. button_align_center.setColorFilter(
  40. ContextCompat.getColor(this@RichTextActivity,
  41. R.color.colorPrimary
  42. ))
  43. button_align_left.setColorFilter(
  44. ContextCompat.getColor(this@RichTextActivity,
  45. R.color.tintColor
  46. ))
  47. button_align_right.setColorFilter(
  48. ContextCompat.getColor(this@RichTextActivity,
  49. R.color.tintColor
  50. ))
  51. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_CENTER,true)
  52. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_LEFT,false)
  53. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_RIGHT,false)}else{
  54. button_align_center.setColorFilter(
  55. ContextCompat.getColor(this@RichTextActivity,
  56. R.color.tintColor
  57. ))
  58. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_CENTER,false)}if(flagArr.contains("JUSTIFYLEFT")){
  59. button_align_center.setColorFilter(
  60. ContextCompat.getColor(this@RichTextActivity,
  61. R.color.tintColor
  62. ))
  63. button_align_left.setColorFilter(
  64. ContextCompat.getColor(this@RichTextActivity,
  65. R.color.colorPrimary
  66. ))
  67. button_align_right.setColorFilter(
  68. ContextCompat.getColor(this@RichTextActivity,
  69. R.color.tintColor
  70. ))
  71. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_CENTER,false)
  72. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_LEFT,true)
  73. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_RIGHT,false)}else{
  74. button_align_left.setColorFilter(
  75. ContextCompat.getColor(this@RichTextActivity,
  76. R.color.tintColor
  77. ))
  78. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_LEFT,false)}if(flagArr.contains("JUSTIFYRIGHT")){
  79. button_align_center.setColorFilter(
  80. ContextCompat.getColor(this@RichTextActivity,
  81. R.color.tintColor
  82. ))
  83. button_align_left.setColorFilter(
  84. ContextCompat.getColor(this@RichTextActivity,
  85. R.color.tintColor
  86. ))
  87. button_align_right.setColorFilter(
  88. ContextCompat.getColor(this@RichTextActivity,
  89. R.color.colorPrimary
  90. ))
  91. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_CENTER,false)
  92. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_LEFT,false)
  93. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_RIGHT,true)}else{
  94. button_align_right.setColorFilter(
  95. ContextCompat.getColor(this@RichTextActivity,
  96. R.color.tintColor
  97. ))
  98. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_RIGHT,false)}if(flagArr.contains("UNDERLINE")){
  99. button_underline.setColorFilter(
  100. ContextCompat.getColor(this@RichTextActivity,
  101. R.color.colorPrimary
  102. ))
  103. mEditorMenuFragment.updateActionStates(ActionType.UNDERLINE,true)}else{
  104. button_underline.setColorFilter(
  105. ContextCompat.getColor(this@RichTextActivity,
  106. R.color.tintColor
  107. ))
  108. mEditorMenuFragment.updateActionStates(ActionType.UNDERLINE,false)}if(flagArr.contains("ORDEREDLIST")){
  109. button_list_ol.setColorFilter(
  110. ContextCompat.getColor(this@RichTextActivity,
  111. R.color.colorPrimary
  112. ))
  113. button_list_ul.setColorFilter(
  114. ContextCompat.getColor(this@RichTextActivity,
  115. R.color.tintColor
  116. ))
  117. mEditorMenuFragment.updateActionStates(ActionType.ORDERED,true)
  118. mEditorMenuFragment.updateActionStates(ActionType.UNORDERED,false)}else{
  119. button_list_ol.setColorFilter(
  120. ContextCompat.getColor(this@RichTextActivity,
  121. R.color.tintColor
  122. ))
  123. mEditorMenuFragment.updateActionStates(ActionType.ORDERED,false)}if(flagArr.contains("UNORDEREDLIST")){
  124. button_list_ul.setColorFilter(
  125. ContextCompat.getColor(this@RichTextActivity,
  126. R.color.colorPrimary
  127. ))
  128. button_list_ol.setColorFilter(
  129. ContextCompat.getColor(this@RichTextActivity,
  130. R.color.tintColor
  131. ))
  132. mEditorMenuFragment.updateActionStates(ActionType.ORDERED,false)
  133. mEditorMenuFragment.updateActionStates(ActionType.UNORDERED,true)}else{
  134. button_list_ul.setColorFilter(
  135. ContextCompat.getColor(this@RichTextActivity,
  136. R.color.tintColor
  137. ))
  138. mEditorMenuFragment.updateActionStates(ActionType.UNORDERED,false)}}

插入图片和视频:原本是调用特有的方法实现,但是可定制化不高,我就改成了插入html的形式

  1. publicvoidinsertVideoPercentage(String url,String width,String height){exec("javascript:RE.prepareInsert();");// exec("javascript:RE.insertVideoWH('" + url + "', '" + width + "', '" + height + "');");String testStr ="<video src=\""+ url +"\" width=\""+ width +"\" height=\""+ height +"\" controls></video><br>";exec("javascript:RE.insertHTML('"+ testStr +"');");}publicvoidinsertImage(String url,String alt){exec("javascript:RE.prepareInsert();");String testStr ="<img src=\""+ url +"\" alt=\""+ alt +"\" width=\"80%\"><br><br>";exec("javascript:RE.insertHTML('"+ testStr +"');");}
  1. // 以下图片视频地址,来自网络素材funselectImage(){// 进入相册选择照片之后可以本地文件直接插入html,或者上传到服务器获取地址再插入html中val path="https://upload-images.jianshu.io/upload_images/5809200-a99419bb94924e6d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"againEdit()
  2. rich_Editor.insertImage(path,"dachshund")
  3. KeyBoardUtils.openKeyboard(edit_name,this@RichTextActivity)}funselectVideo(){// 选择视频之后可以本地文件直接插入html,或者上传到服务器获取地址再插入html中val path="https://media.w3.org/2010/05/sintel/trailer.mp4"againEdit()
  4. rich_Editor.insertVideoPercentage(path,"80%","30%")
  5. KeyBoardUtils.openKeyboard(edit_name,this@RichTextActivity)}

图片删除:插入图片后点击图片可将图片删除:

  1. @SuppressLint("ClickableViewAccessibility")publicvoidsetImageClickListener(ImageClickListener imageClickListener){this.imageClickListener = imageClickListener;if(this.imageClickListener !=null){RichEditor.this.setOnTouchListener(newOnTouchListener(){@OverridepublicbooleanonTouch(View v,MotionEvent event){switch(event.getAction()){caseMotionEvent.ACTION_DOWN:DownX=(int) event.getX();DownY=(int) event.getY();
  2. moveX =0;
  3. moveY =0;
  4. currentMS =System.currentTimeMillis();//long currentMS 获取系统时间break;caseMotionEvent.ACTION_MOVE:
  5. moveX +=Math.abs(event.getX()-DownX);//X轴距离
  6. moveY +=Math.abs(event.getY()-DownY);//y轴距离DownX= event.getX();DownY= event.getY();break;caseMotionEvent.ACTION_UP:long moveTime =System.currentTimeMillis()- currentMS;//移动时间//判断是否继续传递信号if(moveTime <400&&(moveX <25&& moveY <25)){//这里是点击HitTestResult mResult =getHitTestResult();if(mResult !=null){finalint type = mResult.getType();if(type ==HitTestResult.IMAGE_TYPE){//|| type == WebView.HitTestResult.IMAGE_ANCHOR_TYPE || type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE//如果是点击图片String imageUrl = mResult.getExtra();setInputEnabled(false);postDelayed(newRunnable(){@Overridepublicvoidrun(){if(imageClickListener !=null){if(imageUrl.contains("file://")){//说明是本地文件去除String newImageUrl = imageUrl.replace("file://","");
  7. imageClickListener.onImageClick(newImageUrl);}else{
  8. imageClickListener.onImageClick(imageUrl);}}}},200);}else{//不是点击的图片}}}break;}returnfalse;}});}}
  1. view.findViewById<View>(R.id.linear_delete_pic).setOnClickListener{ v: View?->//删除图片val removeUrl ="<img src=\"$currentUrl\" alt=\"dachshund\" width=\"80%\"><br>"val newUrl: String = rich_Editor.html.replace(removeUrl,"")
  2. currentUrl =""
  3. rich_Editor.html = newUrl
  4. if(RichUtils.isEmpty(rich_Editor.html)){
  5. rich_Editor.html =""}
  6. popupWindow?.dismiss()}

插入链接:必须要先执行一次restorerange ,控制用户选择的文本范围或光标的当前位置

  1. RE.restorerange=function(){var selection = window.getSelection();
  2. selection.removeAllRanges();var range = document.createRange();
  3. range.setStart(RE.currentSelection.startContainer,RE.currentSelection.startOffset);
  4. range.setEnd(RE.currentSelection.endContainer,RE.currentSelection.endOffset);
  5. selection.addRange(range);}RE.insertLink=function(url, title){RE.restorerange();var sel = document.getSelection();if(sel.toString().length ==0){
  6. document.execCommand("insertHTML",false,"<a href='"+url+"'>"+title+"</a>");}elseif(sel.rangeCount){var el = document.createElement("a");
  7. el.setAttribute("href", url);
  8. el.setAttribute("title", title);var range = sel.getRangeAt(0).cloneRange();
  9. range.surroundContents(el);
  10. sel.removeAllRanges();
  11. sel.addRange(range);}RE.callback();}

焦点的处理:

(1) 每一次调用排版功能都要调用一次,重新处理输入焦点,避免焦点冲突

  1. privatefunagainEdit(){//如果第一次点击例如加粗,没有焦点时,获取焦点并弹出软键盘
  2. rich_Editor.focusEditor()
  3. KeyBoardUtils.openKeyboard(edit_name,this@RichTextActivity)}
  1. publicvoidfocusEditor(){requestFocus();exec("javascript:RE.focus();");}
  1. RE.focus=function(){var range = document.createRange();
  2. range.selectNodeContents(RE.editor);
  3. range.collapse(false);// var selection = window.getSelection();// selection.removeAllRanges();// selection.addRange(range);RE.editor.focus();}

(2)这里弹出排版页面时必须同时弹出键盘,否则输入框的焦点消失,之前所设置的格式也会随之消失。比如我在底部点了个加粗效果,再点击排版弹窗时,加粗效果应该存在。但是一旦键盘关闭焦点消失,光标的效果也会被重置。
在这里插入图片描述

  1. KeyboardUtils.registerSoftInputChangedListener(this){ height: Int ->//键盘打开
  2. isKeyboardShowing = height >0if(height >0){//fl_action.visibility = View.GONEval params = fl_action.layoutParams
  3. params.height = height/2
  4. fl_action.layoutParams = params
  5. }else{//if (fl_action.visibility != View.VISIBLE)
  6. fl_action.visibility = View.GONE
  7. iv_action.setColorFilter(ContextCompat.getColor(this@RichTextActivity, R.color.tintColor))}}
  8. iv_action.setOnClickListener{if(fl_action.visibility == View.VISIBLE){
  9. fl_action.visibility = View.GONE
  10. iv_action.setColorFilter(ContextCompat.getColor(this@RichTextActivity, R.color.tintColor))}else{// if (isKeyboardShowing) {// KeyboardUtils.hideSoftInput(this@RichTextActivity)// }
  11. KeyboardUtils.showSoftInput(this@RichTextActivity)
  12. fl_action.visibility = View.VISIBLE
  13. iv_action.setColorFilter(ContextCompat.getColor(this@RichTextActivity, R.color.colorPrimary))}}

主要的逻辑代码(RichTextActivity.kt):

  1. class RichTextActivity :AppCompatActivity(), OnActionPerformListener {privatevar popupWindow //编辑图片的pop: CommonPopupWindow?=nullprivatevar currentUrl=""privateval mEditorMenuFragment by lazy{EditorMenuFragment(R.id.fl_action)}privatevar isKeyboardShowing =falseprivateval inputLinkDialog by lazy {InputLinkDialog(this, sureListener ={href, name ->againEdit()
  2. rich_Editor.insertLink(href,name)})}overridefunonCreate(savedInstanceState: Bundle?){super.onCreate(savedInstanceState)setContentView(R.layout.activity_rich_text)initData()}privatefuninitData(){initPop()initEditor()}privatefuninitEditor(){//输入框显示字体的大小
  3. rich_Editor.setEditorFontSize(16)//输入框显示字体的颜色
  4. rich_Editor.setEditorFontColor(ContextCompat.getColor(this,R.color.deepGrey))//输入框背景设置
  5. rich_Editor.setEditorBackgroundColor(Color.WHITE)//输入框文本padding
  6. rich_Editor.setPadding(10,10,10,10)//输入提示文本
  7. rich_Editor.setPlaceholder("点击输入正文!~")//文本输入框监听事件
  8. rich_Editor.setOnTextChangeListener{ text -> Log.e("富文本文字变动", text!!)}
  9. edit_name.addTextChangedListener(object: TextWatcher {overridefunbeforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int){}overridefunonTextChanged(s: CharSequence, start: Int, before: Int, count: Int){}overridefunafterTextChanged(s: Editable){
  10. tv_sum.text = edit_name.text.toString().length.toString()+"/100"val html = rich_Editor.html
  11. }})
  12. rich_Editor.setOnDecorationChangeListener{ _, types ->val flagArr = ArrayList<String>()for(i in types.indices){
  13. flagArr.add(types[i].name)}if(flagArr.contains("BOLD")){
  14. button_bold.setColorFilter(
  15. ContextCompat.getColor(this@RichTextActivity,
  16. R.color.colorPrimary
  17. ))
  18. mEditorMenuFragment.updateActionStates(ActionType.BOLD,true)}else{
  19. button_bold.setColorFilter(
  20. ContextCompat.getColor(this@RichTextActivity,
  21. R.color.tintColor
  22. ))
  23. mEditorMenuFragment.updateActionStates(ActionType.BOLD,false)}if(flagArr.contains("ITALIC")){
  24. button_italic.setColorFilter(
  25. ContextCompat.getColor(this@RichTextActivity,
  26. R.color.colorPrimary
  27. ))
  28. mEditorMenuFragment.updateActionStates(ActionType.ITALIC,true)}else{
  29. button_italic.setColorFilter(
  30. ContextCompat.getColor(this@RichTextActivity,
  31. R.color.tintColor
  32. ))
  33. mEditorMenuFragment.updateActionStates(ActionType.ITALIC,false)}if(flagArr.contains("STRIKETHROUGH")){
  34. button_strikethrough.setColorFilter(
  35. ContextCompat.getColor(this@RichTextActivity,
  36. R.color.colorPrimary
  37. ))
  38. mEditorMenuFragment.updateActionStates(ActionType.STRIKETHROUGH,true)}else{
  39. button_strikethrough.setColorFilter(
  40. ContextCompat.getColor(this@RichTextActivity,
  41. R.color.tintColor
  42. ))
  43. mEditorMenuFragment.updateActionStates(ActionType.STRIKETHROUGH,false)}if(flagArr.contains("JUSTIFYCENTER")){
  44. button_align_center.setColorFilter(
  45. ContextCompat.getColor(this@RichTextActivity,
  46. R.color.colorPrimary
  47. ))
  48. button_align_left.setColorFilter(
  49. ContextCompat.getColor(this@RichTextActivity,
  50. R.color.tintColor
  51. ))
  52. button_align_right.setColorFilter(
  53. ContextCompat.getColor(this@RichTextActivity,
  54. R.color.tintColor
  55. ))
  56. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_CENTER,true)
  57. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_LEFT,false)
  58. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_RIGHT,false)}else{
  59. button_align_center.setColorFilter(
  60. ContextCompat.getColor(this@RichTextActivity,
  61. R.color.tintColor
  62. ))
  63. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_CENTER,false)}if(flagArr.contains("JUSTIFYLEFT")){
  64. button_align_center.setColorFilter(
  65. ContextCompat.getColor(this@RichTextActivity,
  66. R.color.tintColor
  67. ))
  68. button_align_left.setColorFilter(
  69. ContextCompat.getColor(this@RichTextActivity,
  70. R.color.colorPrimary
  71. ))
  72. button_align_right.setColorFilter(
  73. ContextCompat.getColor(this@RichTextActivity,
  74. R.color.tintColor
  75. ))
  76. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_CENTER,false)
  77. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_LEFT,true)
  78. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_RIGHT,false)}else{
  79. button_align_left.setColorFilter(
  80. ContextCompat.getColor(this@RichTextActivity,
  81. R.color.tintColor
  82. ))
  83. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_LEFT,false)}if(flagArr.contains("JUSTIFYRIGHT")){
  84. button_align_center.setColorFilter(
  85. ContextCompat.getColor(this@RichTextActivity,
  86. R.color.tintColor
  87. ))
  88. button_align_left.setColorFilter(
  89. ContextCompat.getColor(this@RichTextActivity,
  90. R.color.tintColor
  91. ))
  92. button_align_right.setColorFilter(
  93. ContextCompat.getColor(this@RichTextActivity,
  94. R.color.colorPrimary
  95. ))
  96. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_CENTER,false)
  97. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_LEFT,false)
  98. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_RIGHT,true)}else{
  99. button_align_right.setColorFilter(
  100. ContextCompat.getColor(this@RichTextActivity,
  101. R.color.tintColor
  102. ))
  103. mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_RIGHT,false)}if(flagArr.contains("UNDERLINE")){
  104. button_underline.setColorFilter(
  105. ContextCompat.getColor(this@RichTextActivity,
  106. R.color.colorPrimary
  107. ))
  108. mEditorMenuFragment.updateActionStates(ActionType.UNDERLINE,true)}else{
  109. button_underline.setColorFilter(
  110. ContextCompat.getColor(this@RichTextActivity,
  111. R.color.tintColor
  112. ))
  113. mEditorMenuFragment.updateActionStates(ActionType.UNDERLINE,false)}if(flagArr.contains("ORDEREDLIST")){
  114. button_list_ol.setColorFilter(
  115. ContextCompat.getColor(this@RichTextActivity,
  116. R.color.colorPrimary
  117. ))
  118. button_list_ul.setColorFilter(
  119. ContextCompat.getColor(this@RichTextActivity,
  120. R.color.tintColor
  121. ))
  122. mEditorMenuFragment.updateActionStates(ActionType.ORDERED,true)
  123. mEditorMenuFragment.updateActionStates(ActionType.UNORDERED,false)}else{
  124. button_list_ol.setColorFilter(
  125. ContextCompat.getColor(this@RichTextActivity,
  126. R.color.tintColor
  127. ))
  128. mEditorMenuFragment.updateActionStates(ActionType.ORDERED,false)}if(flagArr.contains("UNORDEREDLIST")){
  129. button_list_ul.setColorFilter(
  130. ContextCompat.getColor(this@RichTextActivity,
  131. R.color.colorPrimary
  132. ))
  133. button_list_ol.setColorFilter(
  134. ContextCompat.getColor(this@RichTextActivity,
  135. R.color.tintColor
  136. ))
  137. mEditorMenuFragment.updateActionStates(ActionType.ORDERED,false)
  138. mEditorMenuFragment.updateActionStates(ActionType.UNORDERED,true)}else{
  139. button_list_ul.setColorFilter(
  140. ContextCompat.getColor(this@RichTextActivity,
  141. R.color.tintColor
  142. ))
  143. mEditorMenuFragment.updateActionStates(ActionType.UNORDERED,false)}}
  144. rich_Editor.setImageClickListener{ imageUrl ->
  145. currentUrl = imageUrl
  146. popupWindow?.showBottom(ll_container,0.5f)}
  147. button_rich_do.setOnClickListener{ rich_Editor.redo()}
  148. button_rich_undo.setOnClickListener{ rich_Editor.undo()}
  149. button_bold.setOnClickListener{againEdit()
  150. rich_Editor.setBold()}
  151. button_underline.setOnClickListener{againEdit()
  152. rich_Editor.setUnderline()}
  153. button_italic.setOnClickListener{againEdit()
  154. rich_Editor.setItalic()}
  155. button_strikethrough.setOnClickListener{againEdit()
  156. rich_Editor.setStrikeThrough()}//字号
  157. button_h1.setOnClickListener{againEdit()
  158. rich_Editor.setFontSize(2)}
  159. button_h2.setOnClickListener{againEdit()
  160. rich_Editor.setFontSize(3)}
  161. button_h3.setOnClickListener{againEdit()
  162. rich_Editor.setFontSize(4)}
  163. button_h4.setOnClickListener{againEdit()
  164. rich_Editor.setFontSize(5)}
  165. button_indent_decrease.setOnClickListener{againEdit()
  166. rich_Editor.setOutdent()}
  167. button_indent_increase.setOnClickListener{againEdit()
  168. rich_Editor.setIndent()}
  169. button_list_ul.setOnClickListener{againEdit()
  170. rich_Editor.setBullets()}
  171. button_list_ol.setOnClickListener{againEdit()
  172. rich_Editor.setNumbers()}
  173. button_align_center.setOnClickListener{againEdit()
  174. rich_Editor.setAlignCenter()}
  175. button_align_left.setOnClickListener{againEdit()
  176. rich_Editor.setAlignLeft()}
  177. button_align_right.setOnClickListener{againEdit()
  178. rich_Editor.setAlignRight()}
  179. button_link.setOnClickListener{
  180. KeyboardUtils.hideSoftInput(this@RichTextActivity)
  181. inputLinkDialog.show()}//选择图片
  182. button_image.setOnClickListener{selectImage()}//选择视频
  183. button_video.setOnClickListener{selectVideo()}
  184. mEditorMenuFragment.setActionClickListener(this)
  185. supportFragmentManager.beginTransaction().add(R.id.fl_action, mEditorMenuFragment, EditorMenuFragment::class.java.name).commit()
  186. KeyboardUtils.registerSoftInputChangedListener(this){ height: Int ->//键盘打开
  187. isKeyboardShowing = height >0if(height >0){//fl_action.visibility = View.GONEval params = fl_action.layoutParams
  188. params.height = height/2
  189. fl_action.layoutParams = params
  190. }else{//if (fl_action.visibility != View.VISIBLE)
  191. fl_action.visibility = View.GONE
  192. iv_action.setColorFilter(ContextCompat.getColor(this@RichTextActivity, R.color.tintColor))}}
  193. iv_action.setOnClickListener{if(fl_action.visibility == View.VISIBLE){
  194. fl_action.visibility = View.GONE
  195. iv_action.setColorFilter(ContextCompat.getColor(this@RichTextActivity, R.color.tintColor))}else{// if (isKeyboardShowing) {// KeyboardUtils.hideSoftInput(this@RichTextActivity)// }
  196. KeyboardUtils.showSoftInput(this@RichTextActivity)
  197. fl_action.visibility = View.VISIBLE
  198. iv_action.setColorFilter(ContextCompat.getColor(this@RichTextActivity, R.color.colorPrimary))}}}privatefuninitPop(){val view= LayoutInflater.from(this@RichTextActivity).inflate(R.layout.pop_picture,null)
  199. view.findViewById<View>(R.id.linear_cancle).setOnClickListener{ popupWindow?.dismiss()}
  200. view.findViewById<View>(R.id.linear_delete_pic).setOnClickListener{ v: View?->//删除图片val removeUrl ="<img src=\"$currentUrl\" alt=\"dachshund\" width=\"80%\"><br>"val newUrl: String = rich_Editor.html.replace(removeUrl,"")
  201. currentUrl =""
  202. rich_Editor.html = newUrl
  203. if(RichUtils.isEmpty(rich_Editor.html)){
  204. rich_Editor.html =""}
  205. popupWindow?.dismiss()}
  206. popupWindow = CommonPopupWindow.Builder(this@RichTextActivity).setView(view).setWidthAndHeight(
  207. ViewGroup.LayoutParams.MATCH_PARENT,
  208. ViewGroup.LayoutParams.WRAP_CONTENT
  209. ).setOutsideTouchable(true)//在外不可用手指取消.setAnimationStyle(R.style.pop_animation)//设置popWindow的出场动画.create()
  210. popupWindow?.setOnDismissListener(PopupWindow.OnDismissListener{
  211. rich_Editor.setInputEnabled(true)})}privatefunagainEdit(){//如果第一次点击例如加粗,没有焦点时,获取焦点并弹出软键盘
  212. rich_Editor.focusEditor()
  213. KeyBoardUtils.openKeyboard(edit_name,this@RichTextActivity)}overridefunonActionPerform(type: ActionType?,vararg values: Any?){var value =""if(values !=null&& values.isNotEmpty()){
  214. value = values[0]as String
  215. }againEdit()when(type){
  216. ActionType.FORE_COLOR ->{ rich_Editor.setTextColor(value)}
  217. ActionType.BOLD ->{ rich_Editor.setBold()}
  218. ActionType.ITALIC -> rich_Editor.setItalic()
  219. ActionType.UNDERLINE -> rich_Editor.setUnderline()
  220. ActionType.STRIKETHROUGH -> rich_Editor.setStrikeThrough()
  221. ActionType.H1 ->{
  222. rich_Editor.setFontSize(2)
  223. mEditorMenuFragment.updateActionStates(ActionType.H1,true)
  224. mEditorMenuFragment.updateActionStates(ActionType.H2,false)
  225. mEditorMenuFragment.updateActionStates(ActionType.H3,false)
  226. mEditorMenuFragment.updateActionStates(ActionType.H4,false)}
  227. ActionType.H2 ->{
  228. rich_Editor.setFontSize(3)
  229. mEditorMenuFragment.updateActionStates(ActionType.H1,false)
  230. mEditorMenuFragment.updateActionStates(ActionType.H2,true)
  231. mEditorMenuFragment.updateActionStates(ActionType.H3,false)
  232. mEditorMenuFragment.updateActionStates(ActionType.H4,false)}
  233. ActionType.H3 ->{
  234. rich_Editor.setFontSize(4)
  235. mEditorMenuFragment.updateActionStates(ActionType.H1,false)
  236. mEditorMenuFragment.updateActionStates(ActionType.H2,false)
  237. mEditorMenuFragment.updateActionStates(ActionType.H3,true)
  238. mEditorMenuFragment.updateActionStates(ActionType.H4,false)}
  239. ActionType.H4 ->{
  240. rich_Editor.setFontSize(5)
  241. mEditorMenuFragment.updateActionStates(ActionType.H1,false)
  242. mEditorMenuFragment.updateActionStates(ActionType.H2,false)
  243. mEditorMenuFragment.updateActionStates(ActionType.H3,false)
  244. mEditorMenuFragment.updateActionStates(ActionType.H4,true)}
  245. ActionType.JUSTIFY_LEFT -> rich_Editor.setAlignLeft()
  246. ActionType.JUSTIFY_CENTER -> rich_Editor.setAlignCenter()
  247. ActionType.JUSTIFY_RIGHT -> rich_Editor.setAlignRight()
  248. ActionType.INDENT -> rich_Editor.setIndent()
  249. ActionType.OUTDENT -> rich_Editor.setOutdent()
  250. ActionType.UNORDERED->rich_Editor.setBullets()
  251. ActionType.ORDERED->rich_Editor.setNumbers()
  252. ActionType.IMAGE ->{selectImage()}
  253. ActionType.LINK ->{
  254. KeyboardUtils.hideSoftInput(this@RichTextActivity)
  255. inputLinkDialog.show()}
  256. ActionType.VIDEO ->{selectVideo()}else->{}}}// 以下图片视频地址,来自网络素材funselectImage(){// 进入相册选择照片之后可以本地文件直接插入html,或者上传到服务器获取地址再插入html中val path="https://upload-images.jianshu.io/upload_images/5809200-a99419bb94924e6d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"againEdit()
  257. rich_Editor.insertImage(path,"dachshund")
  258. KeyBoardUtils.openKeyboard(edit_name,this@RichTextActivity)}funselectVideo(){// 选择视频之后可以本地文件直接插入html,或者上传到服务器获取地址再插入html中val path="https://media.w3.org/2010/05/sintel/trailer.mp4"againEdit()
  259. rich_Editor.insertVideoPercentage(path,"80%","30%")
  260. KeyBoardUtils.openKeyboard(edit_name,this@RichTextActivity)}}

4. 完整代码

完整代码我已上传github,需要的可以自行pull:https://github.com/FullCourage/RichEditor


总结

这个功能是之前做的,时间间隔有点久了,有些细微的点已经记不得了,由此事后的总结记录就格外重要。

标签: android kotlin webview

本文转载自: https://blog.csdn.net/T01151018/article/details/139629659
版权归原作者 full courage 所有, 如有侵权,请联系我们删除。

“【Android】基于webView打造富文本编辑器(H5)”的评论:

还没有评论