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

饭宝  金牌会员 | 2024-9-13 07:15:05 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 897|帖子 897|积分 2691


前言

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. public class RichEditor extends WebView {
  2.         private static final String SETUP_HTML = "file:///android_asset/editor.html";
  3.     public enum Type {
  4.         BOLD,
  5.         ITALIC,
  6.         SUBSCRIPT,
  7.         SUPERSCRIPT,
  8.         STRIKETHROUGH,
  9.         UNDERLINE,
  10.         H1,
  11.         H2,
  12.         H3,
  13.         H4,
  14.         H5,
  15.         H6,
  16.         ORDEREDLIST,
  17.         UNORDEREDLIST,
  18.         JUSTIFYCENTER,
  19.         JUSTIFYFULL,
  20.         JUSTIFYLEFT,
  21.         JUSTIFYRIGHT
  22.     }
  23.     @SuppressLint("SetJavaScriptEnabled")
  24.     public RichEditor(Context context, AttributeSet attrs, int defStyleAttr) {
  25.         super(context, attrs, defStyleAttr);
  26.         setVerticalScrollBarEnabled(false);
  27.         setHorizontalScrollBarEnabled(false);
  28.         getSettings().setJavaScriptEnabled(true);
  29.         setWebChromeClient(new WebChromeClient());
  30.         setWebViewClient(createWebviewClient());
  31.         loadUrl(SETUP_HTML);
  32.         applyAttributes(context, attrs);
  33.     }
  34. }
复制代码
其中​editor.html​即编辑器所展示的主页面
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta name="viewport" content="user-scalable=no">
  5.     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  6.     <link rel="stylesheet" type="text/css" href="normalize.css">
  7.     <link rel="stylesheet" type="text/css" href="style.css">
  8. </head>
  9. <body>
  10. <div id="editor" contenteditable="true"></div>
  11. <script type="text/javascript" src="rich_editor.js"></script>
  12. </body>
  13. </html>
复制代码
RichEditor控件中的一个功能就对应着​rich_editor.js​中的一个函数,比方加粗结果:
RichEditor:
  1. public void setBold() {
  2.         exec("javascript:RE.setBold();");
  3.     }
复制代码
​rich_editor.js:
  1. RE.setBold = function() {
  2.     document.execCommand('bold', false, null);
  3.     RE.enabledEditingItems()
  4. }
复制代码
最后,RichEditor在布局中使用:
  1. <com.text.richeditor.editor.RichEditor
  2.         android:id="@+id/rich_Editor"
  3.         android:layout_width="match_parent"
  4.         android:layout_height="0dp"
  5.         android:layout_weight="1"
  6.         android:overScrollMode="never" />
复制代码
主要样式如下:

除了底部的功能栏可以使用,最左边还有一个排版按钮,这里主要是为了不方便弹出底部时,可以选择使用弹窗弹出功能栏,功能是同等的,可以选择性阉割。使用某一功能时会有颜色高亮提示。
3. 功能调用

首先要在activity中初始化控件,实现setOnDecorationChangeListener接口,监听点击某个功能按钮时表现对应高亮,让用户看到如今正在所使用的排版格式。
  1.                 //输入框显示字体的大小
  2.         rich_Editor.setEditorFontSize(16)
  3.         //输入框显示字体的颜色
  4.         rich_Editor.setEditorFontColor(ContextCompat.getColor(this,R.color.deepGrey))
  5.         //输入框背景设置
  6.         rich_Editor.setEditorBackgroundColor(Color.WHITE)
  7.         //输入框文本padding
  8.         rich_Editor.setPadding(10, 10, 10, 10)
  9.         //输入提示文本
  10.         rich_Editor.setPlaceholder("点击输入正文!~")
  11.         rich_Editor.setOnDecorationChangeListener { _, types ->
  12.             val flagArr = ArrayList<String>()
  13.             for (i in types.indices) {
  14.                 flagArr.add(types[i].name)
  15.             }
  16.             if (flagArr.contains("BOLD")) {
  17.                 button_bold.setColorFilter(
  18.                     ContextCompat.getColor(
  19.                         this@RichTextActivity,
  20.                         R.color.colorPrimary
  21.                     )
  22.                 )
  23.                 mEditorMenuFragment.updateActionStates(ActionType.BOLD, true)
  24.             } else {
  25.                 button_bold.setColorFilter(
  26.                     ContextCompat.getColor(
  27.                         this@RichTextActivity,
  28.                         R.color.tintColor
  29.                     )
  30.                 )
  31.                 mEditorMenuFragment.updateActionStates(ActionType.BOLD, false)
  32.             }
  33.             if (flagArr.contains("ITALIC")) {
  34.                 button_italic.setColorFilter(
  35.                     ContextCompat.getColor(
  36.                         this@RichTextActivity,
  37.                         R.color.colorPrimary
  38.                     )
  39.                 )
  40.                 mEditorMenuFragment.updateActionStates(ActionType.ITALIC, true)
  41.             } else {
  42.                 button_italic.setColorFilter(
  43.                     ContextCompat.getColor(
  44.                         this@RichTextActivity,
  45.                         R.color.tintColor
  46.                     )
  47.                 )
  48.                 mEditorMenuFragment.updateActionStates(ActionType.ITALIC, false)
  49.             }
  50.             if (flagArr.contains("STRIKETHROUGH")) {
  51.                 button_strikethrough.setColorFilter(
  52.                     ContextCompat.getColor(
  53.                         this@RichTextActivity,
  54.                         R.color.colorPrimary
  55.                     )
  56.                 )
  57.                 mEditorMenuFragment.updateActionStates(ActionType.STRIKETHROUGH, true)
  58.             } else {
  59.                 button_strikethrough.setColorFilter(
  60.                     ContextCompat.getColor(
  61.                         this@RichTextActivity,
  62.                         R.color.tintColor
  63.                     )
  64.                 )
  65.                 mEditorMenuFragment.updateActionStates(ActionType.STRIKETHROUGH, false)
  66.             }
  67.             if (flagArr.contains("JUSTIFYCENTER")) {
  68.                 button_align_center.setColorFilter(
  69.                     ContextCompat.getColor(
  70.                         this@RichTextActivity,
  71.                         R.color.colorPrimary
  72.                     )
  73.                 )
  74.                 button_align_left.setColorFilter(
  75.                     ContextCompat.getColor(
  76.                         this@RichTextActivity,
  77.                         R.color.tintColor
  78.                     )
  79.                 )
  80.                 button_align_right.setColorFilter(
  81.                     ContextCompat.getColor(
  82.                         this@RichTextActivity,
  83.                         R.color.tintColor
  84.                     )
  85.                 )
  86.                 mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_CENTER, true)
  87.                 mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_LEFT, false)
  88.                 mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_RIGHT, false)
  89.             } else {
  90.                 button_align_center.setColorFilter(
  91.                     ContextCompat.getColor(
  92.                         this@RichTextActivity,
  93.                         R.color.tintColor
  94.                     )
  95.                 )
  96.                 mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_CENTER, false)
  97.             }
  98.             if (flagArr.contains("JUSTIFYLEFT")) {
  99.                 button_align_center.setColorFilter(
  100.                     ContextCompat.getColor(
  101.                         this@RichTextActivity,
  102.                         R.color.tintColor
  103.                     )
  104.                 )
  105.                 button_align_left.setColorFilter(
  106.                     ContextCompat.getColor(
  107.                         this@RichTextActivity,
  108.                         R.color.colorPrimary
  109.                     )
  110.                 )
  111.                 button_align_right.setColorFilter(
  112.                     ContextCompat.getColor(
  113.                         this@RichTextActivity,
  114.                         R.color.tintColor
  115.                     )
  116.                 )
  117.                 mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_CENTER, false)
  118.                 mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_LEFT, true)
  119.                 mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_RIGHT, false)
  120.             } else {
  121.                 button_align_left.setColorFilter(
  122.                     ContextCompat.getColor(
  123.                         this@RichTextActivity,
  124.                         R.color.tintColor
  125.                     )
  126.                 )
  127.                 mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_LEFT, false)
  128.             }
  129.             if (flagArr.contains("JUSTIFYRIGHT")) {
  130.                 button_align_center.setColorFilter(
  131.                     ContextCompat.getColor(
  132.                         this@RichTextActivity,
  133.                         R.color.tintColor
  134.                     )
  135.                 )
  136.                 button_align_left.setColorFilter(
  137.                     ContextCompat.getColor(
  138.                         this@RichTextActivity,
  139.                         R.color.tintColor
  140.                     )
  141.                 )
  142.                 button_align_right.setColorFilter(
  143.                     ContextCompat.getColor(
  144.                         this@RichTextActivity,
  145.                         R.color.colorPrimary
  146.                     )
  147.                 )
  148.                 mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_CENTER, false)
  149.                 mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_LEFT, false)
  150.                 mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_RIGHT, true)
  151.             } else {
  152.                 button_align_right.setColorFilter(
  153.                     ContextCompat.getColor(
  154.                         this@RichTextActivity,
  155.                         R.color.tintColor
  156.                     )
  157.                 )
  158.                 mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_RIGHT, false)
  159.             }
  160.             if (flagArr.contains("UNDERLINE")) {
  161.                 button_underline.setColorFilter(
  162.                     ContextCompat.getColor(
  163.                         this@RichTextActivity,
  164.                         R.color.colorPrimary
  165.                     )
  166.                 )
  167.                 mEditorMenuFragment.updateActionStates(ActionType.UNDERLINE, true)
  168.             } else {
  169.                 button_underline.setColorFilter(
  170.                     ContextCompat.getColor(
  171.                         this@RichTextActivity,
  172.                         R.color.tintColor
  173.                     )
  174.                 )
  175.                 mEditorMenuFragment.updateActionStates(ActionType.UNDERLINE, false)
  176.             }
  177.             if (flagArr.contains("ORDEREDLIST")) {
  178.                 button_list_ol.setColorFilter(
  179.                     ContextCompat.getColor(
  180.                         this@RichTextActivity,
  181.                         R.color.colorPrimary
  182.                     )
  183.                 )
  184.                 button_list_ul.setColorFilter(
  185.                     ContextCompat.getColor(
  186.                         this@RichTextActivity,
  187.                         R.color.tintColor
  188.                     )
  189.                 )
  190.                 mEditorMenuFragment.updateActionStates(ActionType.ORDERED, true)
  191.                 mEditorMenuFragment.updateActionStates(ActionType.UNORDERED, false)
  192.             } else {
  193.                 button_list_ol.setColorFilter(
  194.                     ContextCompat.getColor(
  195.                         this@RichTextActivity,
  196.                         R.color.tintColor
  197.                     )
  198.                 )
  199.                 mEditorMenuFragment.updateActionStates(ActionType.ORDERED, false)
  200.             }
  201.             if (flagArr.contains("UNORDEREDLIST")) {
  202.                 button_list_ul.setColorFilter(
  203.                     ContextCompat.getColor(
  204.                         this@RichTextActivity,
  205.                         R.color.colorPrimary
  206.                     )
  207.                 )
  208.                 button_list_ol.setColorFilter(
  209.                     ContextCompat.getColor(
  210.                         this@RichTextActivity,
  211.                         R.color.tintColor
  212.                     )
  213.                 )
  214.                 mEditorMenuFragment.updateActionStates(ActionType.ORDERED, false)
  215.                 mEditorMenuFragment.updateActionStates(ActionType.UNORDERED, true)
  216.             } else {
  217.                 button_list_ul.setColorFilter(
  218.                     ContextCompat.getColor(
  219.                         this@RichTextActivity,
  220.                         R.color.tintColor
  221.                     )
  222.                 )
  223.                 mEditorMenuFragment.updateActionStates(ActionType.UNORDERED, false)
  224.             }
  225.         }
复制代码
插入图片和视频:本来是调用特有的方法实现,但是可定制化不高,我就改成了插入html的形式
  1. public void insertVideoPercentage(String url, String width, String height) {
  2.         exec("javascript:RE.prepareInsert();");
  3. //        exec("javascript:RE.insertVideoWH('" + url + "', '" + width + "', '" + height + "');");
  4.         String testStr = "<video src="" + url + "" width=""+ width +""  height=""+ height +"" controls></video><br>";
  5.         exec("javascript:RE.insertHTML('" + testStr + "');");
  6.     }
  7. public void insertImage(String url, String alt) {
  8.         exec("javascript:RE.prepareInsert();");
  9.         String testStr = "<img src="" + url + "" alt="" + alt + "" width="80%"><br><br>";
  10.         exec("javascript:RE.insertHTML('" + testStr + "');");
  11.     }
复制代码
  1.         // 以下图片视频地址,来自网络素材
  2.     fun selectImage(){
  3.         // 进入相册选择照片之后可以本地文件直接插入html,或者上传到服务器获取地址再插入html中
  4.         val path="https://upload-images.jianshu.io/upload_images/5809200-a99419bb94924e6d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"
  5.         againEdit()
  6.         rich_Editor.insertImage(path, "dachshund")
  7.         KeyBoardUtils.openKeyboard(edit_name, this@RichTextActivity)
  8.     }
  9.     fun selectVideo(){
  10.         // 选择视频之后可以本地文件直接插入html,或者上传到服务器获取地址再插入html中
  11.         val path="https://media.w3.org/2010/05/sintel/trailer.mp4"
  12.         againEdit()
  13.         rich_Editor.insertVideoPercentage(path, "80%","30%")
  14.         KeyBoardUtils.openKeyboard(edit_name, this@RichTextActivity)
  15.     }
复制代码
图片删除:插入图片后点击图片可将图片删除:
  1. @SuppressLint("ClickableViewAccessibility")
  2.     public void setImageClickListener(ImageClickListener imageClickListener) {
  3.         this.imageClickListener = imageClickListener;
  4.         if (this.imageClickListener != null) {
  5.             RichEditor.this.setOnTouchListener(new OnTouchListener() {
  6.                 @Override
  7.                 public boolean onTouch(View v, MotionEvent event) {
  8.                     switch (event.getAction()) {
  9.                         case MotionEvent.ACTION_DOWN:
  10.                             DownX = (int) event.getX();
  11.                             DownY = (int) event.getY();
  12.                             moveX = 0;
  13.                             moveY = 0;
  14.                             currentMS = System.currentTimeMillis();//long currentMS     获取系统时间
  15.                             break;
  16.                         case MotionEvent.ACTION_MOVE:
  17.                             moveX += Math.abs(event.getX() - DownX);//X轴距离
  18.                             moveY += Math.abs(event.getY() - DownY);//y轴距离
  19.                             DownX = event.getX();
  20.                             DownY = event.getY();
  21.                             break;
  22.                         case MotionEvent.ACTION_UP:
  23.                             long moveTime = System.currentTimeMillis() - currentMS;//移动时间
  24.                             //判断是否继续传递信号
  25.                             if (moveTime < 400 && (moveX < 25 && moveY < 25)) {
  26.                                 //这里是点击
  27.                                 HitTestResult mResult = getHitTestResult();
  28.                                 if (mResult != null) {
  29.                                     final int type = mResult.getType();
  30.                                     if (type == HitTestResult.IMAGE_TYPE) {//|| type == WebView.HitTestResult.IMAGE_ANCHOR_TYPE || type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE
  31.                                         //如果是点击图片
  32.                                         String imageUrl = mResult.getExtra();
  33.                                         setInputEnabled(false);
  34.                                         postDelayed(new Runnable() {
  35.                                             @Override
  36.                                             public void run() {
  37.                                                 if (imageClickListener != null) {
  38.                                                     if (imageUrl.contains("file://")) {
  39.                                                         //说明是本地文件去除
  40.                                                         String newImageUrl = imageUrl.replace("file://", "");
  41.                                                         imageClickListener.onImageClick(newImageUrl);
  42.                                                     } else {
  43.                                                         imageClickListener.onImageClick(imageUrl);
  44.                                                     }
  45.                                                 }
  46.                                             }
  47.                                         }, 200);
  48.                                     } else {
  49.                                         //不是点击的图片
  50.                                     }
  51.                                 }
  52.                             }
  53.                             break;
  54.                     }
  55.                     return false;
  56.                 }
  57.             });
  58.         }
  59.     }
复制代码
  1. view.findViewById<View>(R.id.linear_delete_pic).setOnClickListener { v: View? ->
  2.             //删除图片
  3.             val removeUrl =
  4.                 "<img src="$currentUrl" alt="dachshund" width="80%"><br>"
  5.             val newUrl: String = rich_Editor.html.replace(removeUrl, "")
  6.             currentUrl = ""
  7.             rich_Editor.html = newUrl
  8.             if (RichUtils.isEmpty(rich_Editor.html)) {
  9.                 rich_Editor.html = ""
  10.             }
  11.             popupWindow?.dismiss()
  12.         }
复制代码
插入链接:必须要先执行一次restorerange ,控制用户选择的文本范围或光标的当前位置
  1. RE.restorerange = function(){
  2.     var selection = window.getSelection();
  3.     selection.removeAllRanges();
  4.     var range = document.createRange();
  5.     range.setStart(RE.currentSelection.startContainer, RE.currentSelection.startOffset);
  6.     range.setEnd(RE.currentSelection.endContainer, RE.currentSelection.endOffset);
  7.     selection.addRange(range);
  8. }
  9. RE.insertLink = function(url, title) {
  10.     RE.restorerange();
  11.     var sel = document.getSelection();
  12.     if (sel.toString().length == 0) {
  13.         document.execCommand("insertHTML",false,"<a href='"+url+"'>"+title+"</a>");
  14.     } else if (sel.rangeCount) {
  15.        var el = document.createElement("a");
  16.        el.setAttribute("href", url);
  17.        el.setAttribute("title", title);
  18.        var range = sel.getRangeAt(0).cloneRange();
  19.        range.surroundContents(el);
  20.        sel.removeAllRanges();
  21.        sel.addRange(range);
  22.    }
  23.     RE.callback();
  24. }
复制代码
核心的处置惩罚:
(1) 每一次调用排版功能都要调用一次,重新处置惩罚输入核心,避免核心冲突
  1. private fun againEdit() {
  2.         //如果第一次点击例如加粗,没有焦点时,获取焦点并弹出软键盘
  3.         rich_Editor.focusEditor()
  4.         KeyBoardUtils.openKeyboard(edit_name, this@RichTextActivity)
  5.     }
复制代码
  1. public void focusEditor() {
  2.         requestFocus();
  3.         exec("javascript:RE.focus();");
  4.     }
复制代码
  1. RE.focus = function() {
  2.     var range = document.createRange();
  3.     range.selectNodeContents(RE.editor);
  4.     range.collapse(false);
  5. //    var selection = window.getSelection();
  6. //    selection.removeAllRanges();
  7. //    selection.addRange(range);
  8.     RE.editor.focus();
  9. }
复制代码
(2)这里弹出排版页面时必须同时弹出键盘,否则输入框的核心消失,之前所设置的格式也会随之消失。好比我在底部点了个加粗结果,再点击排版弹窗时,加粗结果应该存在。但是一旦键盘关闭核心消失,光标的结果也会被重置。

  1.         KeyboardUtils.registerSoftInputChangedListener(this) { height: Int ->
  2.             //键盘打开
  3.             isKeyboardShowing = height > 0
  4.             if (height > 0) {
  5.                 //fl_action.visibility = View.GONE
  6.                 val params = fl_action.layoutParams
  7.                 params.height = height/2
  8.                 fl_action.layoutParams = params
  9.             } else{
  10.                 //if (fl_action.visibility != View.VISIBLE)
  11.                 fl_action.visibility = View.GONE
  12.                 iv_action.setColorFilter(ContextCompat.getColor(this@RichTextActivity, R.color.tintColor))
  13.             }
  14.         }
  15.         iv_action.setOnClickListener {
  16.             if (fl_action.visibility == View.VISIBLE) {
  17.                 fl_action.visibility = View.GONE
  18.                 iv_action.setColorFilter(ContextCompat.getColor(this@RichTextActivity, R.color.tintColor))
  19.             } else {
  20. //                if (isKeyboardShowing) {
  21. //                    KeyboardUtils.hideSoftInput(this@RichTextActivity)
  22. //                }
  23.                 KeyboardUtils.showSoftInput(this@RichTextActivity)
  24.                 fl_action.visibility = View.VISIBLE
  25.                 iv_action.setColorFilter(ContextCompat.getColor(this@RichTextActivity, R.color.colorPrimary))
  26.             }
  27.         }
复制代码
主要的逻辑代码(RichTextActivity.kt):
  1. class RichTextActivity :AppCompatActivity(), OnActionPerformListener {    private var popupWindow //编辑图片的pop            : CommonPopupWindow? = null    private var currentUrl=""    private val mEditorMenuFragment by lazy{ EditorMenuFragment(R.id.fl_action) }    private var isKeyboardShowing = false    private val inputLinkDialog by lazy { InputLinkDialog(this, sureListener = {href, name ->        againEdit()        rich_Editor.insertLink(href,name)    }) }    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_rich_text)        initData()    }    private fun initData(){        initPop()        initEditor()    }    private fun initEditor() {        //输入框表现字体的大小        rich_Editor.setEditorFontSize(16)        //输入框表现字体的颜色        rich_Editor.setEditorFontColor(ContextCompat.getColor(this,R.color.deepGrey))        //输入框背景设置        rich_Editor.setEditorBackgroundColor(Color.WHITE)        //输入框文本padding        rich_Editor.setPadding(10, 10, 10, 10)        //输入提示文本        rich_Editor.setPlaceholder("点击输入正文!~")        //文本输入框监听变乱        rich_Editor.setOnTextChangeListener { text -> Log.e("富文本文字变更", text!!) }        edit_name.addTextChangedListener(object : TextWatcher {            override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}            override fun afterTextChanged(s: Editable) {                tv_sum.text = edit_name.text.toString().length.toString()+"/100"                val html = rich_Editor.html            }        })        rich_Editor.setOnDecorationChangeListener { _, types ->            val flagArr = ArrayList<String>()            for (i in types.indices) {                flagArr.add(types[i].name)            }            if (flagArr.contains("BOLD")) {                button_bold.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.colorPrimary                    )                )                mEditorMenuFragment.updateActionStates(ActionType.BOLD, true)            } else {                button_bold.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.tintColor                    )                )                mEditorMenuFragment.updateActionStates(ActionType.BOLD, false)            }            if (flagArr.contains("ITALIC")) {                button_italic.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.colorPrimary                    )                )                mEditorMenuFragment.updateActionStates(ActionType.ITALIC, true)            } else {                button_italic.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.tintColor                    )                )                mEditorMenuFragment.updateActionStates(ActionType.ITALIC, false)            }            if (flagArr.contains("STRIKETHROUGH")) {                button_strikethrough.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.colorPrimary                    )                )                mEditorMenuFragment.updateActionStates(ActionType.STRIKETHROUGH, true)            } else {                button_strikethrough.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.tintColor                    )                )                mEditorMenuFragment.updateActionStates(ActionType.STRIKETHROUGH, false)            }            if (flagArr.contains("JUSTIFYCENTER")) {                button_align_center.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.colorPrimary                    )                )                button_align_left.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.tintColor                    )                )                button_align_right.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.tintColor                    )                )                mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_CENTER, true)                mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_LEFT, false)                mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_RIGHT, false)            } else {                button_align_center.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.tintColor                    )                )                mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_CENTER, false)            }            if (flagArr.contains("JUSTIFYLEFT")) {                button_align_center.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.tintColor                    )                )                button_align_left.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.colorPrimary                    )                )                button_align_right.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.tintColor                    )                )                mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_CENTER, false)                mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_LEFT, true)                mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_RIGHT, false)            } else {                button_align_left.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.tintColor                    )                )                mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_LEFT, false)            }            if (flagArr.contains("JUSTIFYRIGHT")) {                button_align_center.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.tintColor                    )                )                button_align_left.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.tintColor                    )                )                button_align_right.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.colorPrimary                    )                )                mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_CENTER, false)                mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_LEFT, false)                mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_RIGHT, true)            } else {                button_align_right.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.tintColor                    )                )                mEditorMenuFragment.updateActionStates(ActionType.JUSTIFY_RIGHT, false)            }            if (flagArr.contains("UNDERLINE")) {                button_underline.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.colorPrimary                    )                )                mEditorMenuFragment.updateActionStates(ActionType.UNDERLINE, true)            } else {                button_underline.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.tintColor                    )                )                mEditorMenuFragment.updateActionStates(ActionType.UNDERLINE, false)            }            if (flagArr.contains("ORDEREDLIST")) {                button_list_ol.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.colorPrimary                    )                )                button_list_ul.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.tintColor                    )                )                mEditorMenuFragment.updateActionStates(ActionType.ORDERED, true)                mEditorMenuFragment.updateActionStates(ActionType.UNORDERED, false)            } else {                button_list_ol.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.tintColor                    )                )                mEditorMenuFragment.updateActionStates(ActionType.ORDERED, false)            }            if (flagArr.contains("UNORDEREDLIST")) {                button_list_ul.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.colorPrimary                    )                )                button_list_ol.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.tintColor                    )                )                mEditorMenuFragment.updateActionStates(ActionType.ORDERED, false)                mEditorMenuFragment.updateActionStates(ActionType.UNORDERED, true)            } else {                button_list_ul.setColorFilter(                    ContextCompat.getColor(                        this@RichTextActivity,                        R.color.tintColor                    )                )                mEditorMenuFragment.updateActionStates(ActionType.UNORDERED, false)            }        }        rich_Editor.setImageClickListener { imageUrl ->            currentUrl = imageUrl            popupWindow?.showBottom(ll_container, 0.5f)        }        button_rich_do.setOnClickListener { rich_Editor.redo() }        button_rich_undo.setOnClickListener { rich_Editor.undo()}        button_bold.setOnClickListener {            againEdit()            rich_Editor.setBold() }        button_underline.setOnClickListener {            againEdit()            rich_Editor.setUnderline()        }        button_italic.setOnClickListener {            againEdit()            rich_Editor.setItalic()        }        button_strikethrough.setOnClickListener {            againEdit()            rich_Editor.setStrikeThrough()        }        //字号        button_h1.setOnClickListener {            againEdit()            rich_Editor.setFontSize(2)        }        button_h2.setOnClickListener {            againEdit()            rich_Editor.setFontSize(3)        }        button_h3.setOnClickListener {            againEdit()            rich_Editor.setFontSize(4)        }        button_h4.setOnClickListener {            againEdit()            rich_Editor.setFontSize(5)        }        button_indent_decrease.setOnClickListener {            againEdit()            rich_Editor.setOutdent()        }        button_indent_increase.setOnClickListener {            againEdit()            rich_Editor.setIndent()        }        button_list_ul.setOnClickListener {            againEdit()            rich_Editor.setBullets()        }        button_list_ol.setOnClickListener {            againEdit()            rich_Editor.setNumbers()        }        button_align_center.setOnClickListener {            againEdit()            rich_Editor.setAlignCenter()        }        button_align_left.setOnClickListener {            againEdit()            rich_Editor.setAlignLeft()        }        button_align_right.setOnClickListener {            againEdit()            rich_Editor.setAlignRight()        }        button_link.setOnClickListener {            KeyboardUtils.hideSoftInput(this@RichTextActivity)            inputLinkDialog.show()        }        //选择图片        button_image.setOnClickListener {            selectImage()        }        //选择视频        button_video.setOnClickListener {            selectVideo()        }        mEditorMenuFragment.setActionClickListener(this)        supportFragmentManager.beginTransaction().add(R.id.fl_action, mEditorMenuFragment, EditorMenuFragment::class.java.name).commit()        KeyboardUtils.registerSoftInputChangedListener(this) { height: Int ->            //键盘打开            isKeyboardShowing = height > 0            if (height > 0) {                //fl_action.visibility = View.GONE                val params = fl_action.layoutParams                params.height = height/2                fl_action.layoutParams = params            } else{                //if (fl_action.visibility != View.VISIBLE)                fl_action.visibility = View.GONE                iv_action.setColorFilter(ContextCompat.getColor(this@RichTextActivity, R.color.tintColor))            }        }        iv_action.setOnClickListener {            if (fl_action.visibility == View.VISIBLE) {                fl_action.visibility = View.GONE                iv_action.setColorFilter(ContextCompat.getColor(this@RichTextActivity, R.color.tintColor))            } else {//                if (isKeyboardShowing) {//                    KeyboardUtils.hideSoftInput(this@RichTextActivity)//                }                KeyboardUtils.showSoftInput(this@RichTextActivity)                fl_action.visibility = View.VISIBLE                iv_action.setColorFilter(ContextCompat.getColor(this@RichTextActivity, R.color.colorPrimary))            }        }    }    private fun initPop() {        val view= LayoutInflater.from(this@RichTextActivity).inflate(R.layout.pop_picture, null)        view.findViewById<View>(R.id.linear_cancle)            .setOnClickListener { popupWindow?.dismiss() }        view.findViewById<View>(R.id.linear_delete_pic).setOnClickListener { v: View? ->
  2.             //删除图片
  3.             val removeUrl =
  4.                 "<img src="$currentUrl" alt="dachshund" width="80%"><br>"
  5.             val newUrl: String = rich_Editor.html.replace(removeUrl, "")
  6.             currentUrl = ""
  7.             rich_Editor.html = newUrl
  8.             if (RichUtils.isEmpty(rich_Editor.html)) {
  9.                 rich_Editor.html = ""
  10.             }
  11.             popupWindow?.dismiss()
  12.         }
  13.         popupWindow = CommonPopupWindow.Builder(this@RichTextActivity)            .setView(view)            .setWidthAndHeight(                ViewGroup.LayoutParams.MATCH_PARENT,                ViewGroup.LayoutParams.WRAP_CONTENT            )            .setOutsideTouchable(true) //在外不可用手指取消            .setAnimationStyle(R.style.pop_animation) //设置popWindow的进场动画            .create()        popupWindow?.setOnDismissListener(PopupWindow.OnDismissListener {            rich_Editor.setInputEnabled(                true            )        })    }    private fun againEdit() {
  14.         //如果第一次点击例如加粗,没有焦点时,获取焦点并弹出软键盘
  15.         rich_Editor.focusEditor()
  16.         KeyBoardUtils.openKeyboard(edit_name, this@RichTextActivity)
  17.     }
  18.         override fun onActionPerform(type: ActionType?, vararg values: Any?) {        var value = ""        if (values != null && values.isNotEmpty()) {            value = values[0] as String        }        againEdit()        when (type) {            ActionType.FORE_COLOR -> { rich_Editor.setTextColor(value)}            ActionType.BOLD -> { rich_Editor.setBold()}            ActionType.ITALIC -> rich_Editor.setItalic()            ActionType.UNDERLINE -> rich_Editor.setUnderline()            ActionType.STRIKETHROUGH -> rich_Editor.setStrikeThrough()            ActionType.H1 -> {                rich_Editor.setFontSize(2)                mEditorMenuFragment.updateActionStates(ActionType.H1,true)                mEditorMenuFragment.updateActionStates(ActionType.H2,false)                mEditorMenuFragment.updateActionStates(ActionType.H3,false)                mEditorMenuFragment.updateActionStates(ActionType.H4,false)            }            ActionType.H2 -> {                rich_Editor.setFontSize(3)                mEditorMenuFragment.updateActionStates(ActionType.H1,false)                mEditorMenuFragment.updateActionStates(ActionType.H2,true)                mEditorMenuFragment.updateActionStates(ActionType.H3,false)                mEditorMenuFragment.updateActionStates(ActionType.H4,false)            }            ActionType.H3 -> {                rich_Editor.setFontSize(4)                mEditorMenuFragment.updateActionStates(ActionType.H1,false)                mEditorMenuFragment.updateActionStates(ActionType.H2,false)                mEditorMenuFragment.updateActionStates(ActionType.H3,true)                mEditorMenuFragment.updateActionStates(ActionType.H4,false)            }            ActionType.H4 -> {                rich_Editor.setFontSize(5)                mEditorMenuFragment.updateActionStates(ActionType.H1,false)                mEditorMenuFragment.updateActionStates(ActionType.H2,false)                mEditorMenuFragment.updateActionStates(ActionType.H3,false)                mEditorMenuFragment.updateActionStates(ActionType.H4,true)            }            ActionType.JUSTIFY_LEFT -> rich_Editor.setAlignLeft()            ActionType.JUSTIFY_CENTER -> rich_Editor.setAlignCenter()            ActionType.JUSTIFY_RIGHT -> rich_Editor.setAlignRight()            ActionType.INDENT -> rich_Editor.setIndent()            ActionType.OUTDENT -> rich_Editor.setOutdent()            ActionType.UNORDERED->rich_Editor.setBullets()            ActionType.ORDERED->rich_Editor.setNumbers()            ActionType.IMAGE -> {                selectImage()            }            ActionType.LINK -> {                KeyboardUtils.hideSoftInput(this@RichTextActivity)                inputLinkDialog.show()            }            ActionType.VIDEO -> {                selectVideo()            }            else -> {}        }    }    // 以下图片视频地点,来自网络素材    fun selectImage(){        // 进入相册选择照片之后可以本地文件直接插入html,大概上传到服务器获取地点再插入html中        val path="https://upload-images.jianshu.io/upload_images/5809200-a99419bb94924e6d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"        againEdit()        rich_Editor.insertImage(path, "dachshund")        KeyBoardUtils.openKeyboard(edit_name, this@RichTextActivity)    }    fun selectVideo(){        // 选择视频之后可以本地文件直接插入html,大概上传到服务器获取地点再插入html中        val path="https://media.w3.org/2010/05/sintel/trailer.mp4"        againEdit()        rich_Editor.insertVideoPercentage(path, "80%","30%")        KeyBoardUtils.openKeyboard(edit_name, this@RichTextActivity)    }}
复制代码
4. 完备代码

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

总结

这个功能是之前做的,时间间隔有点久了,有些眇小的点已经记不得了,由此过后的总结记录就格外紧张。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

饭宝

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表