饭宝 发表于 2024-9-13 07:15:05

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

前言

HTML5是构建Web内容的一种语言形貌方式。HTML5是Web中核心语言HTML的规范,用户使用任何手段举行网页浏览时看到的内容本来都是HTML格式的,在浏览器中通过一些技能处置惩罚将其转换成为了可识别的信息。
而WebView 是一种嵌入式浏览器,原生APP应用可以用它来展示网络内容。其功能强大,除了具有一般View的属性和设置外,还可以对url请求、页面加载、渲染、页面交互举行强大的处置惩罚.。
https://img-blog.csdnimg.cn/direct/96f813a24e6442789edab3747ef3ab79.png#pic_center
所以,由于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,终极结果还行。鉴戒的第三方库可移步我之前写的文章:常用的第三方开源库汇总
一、实现结果

https://img-blog.csdnimg.cn/direct/a9141ef204264921bd54ba2120e14f2d.png#pic_center
https://img-blog.csdnimg.cn/direct/980816d3def64bfcba4fcb30b5990fb0.jpeg#pic_center
二、具体实现

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

在项目根目录下新建assets文件夹
https://img-blog.csdnimg.cn/direct/6436903e6eac459b8f8ce125a50f134f.png
导入资源:对于熟悉网页端的开发人员来说,修改这些并不难。
https://img-blog.csdnimg.cn/direct/7125ff7a00b043d293b0fda87a4b29be.png
2. 页面计划

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

        private static final String SETUP_HTML = "file:///android_asset/editor.html";
    public enum Type {
      BOLD,
      ITALIC,
      SUBSCRIPT,
      SUPERSCRIPT,
      STRIKETHROUGH,
      UNDERLINE,
      H1,
      H2,
      H3,
      H4,
      H5,
      H6,
      ORDEREDLIST,
      UNORDEREDLIST,
      JUSTIFYCENTER,
      JUSTIFYFULL,
      JUSTIFYLEFT,
      JUSTIFYRIGHT
    }
    @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);
    }
}
其中​editor.html​即编辑器所展示的主页面
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="user-scalable=no">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <link rel="stylesheet" type="text/css" href="normalize.css">
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>

<div id="editor" contenteditable="true"></div>
<script type="text/javascript" src="rich_editor.js"></script>
</body>
</html>
RichEditor控件中的一个功能就对应着​rich_editor.js​中的一个函数,比方加粗结果:
RichEditor:
public void setBold() {
      exec("javascript:RE.setBold();");
    }
​rich_editor.js:
RE.setBold = function() {
    document.execCommand('bold', false, null);
    RE.enabledEditingItems()
}
最后,RichEditor在布局中使用:
<com.text.richeditor.editor.RichEditor
      android:id="@+id/rich_Editor"
      android:layout_width="match_parent"
      android:layout_height="0dp"
      android:layout_weight="1"
      android:overScrollMode="never" />
主要样式如下:
https://img-blog.csdnimg.cn/direct/4ad1c7c6837748be9b57fca1bef3f49d.png
除了底部的功能栏可以使用,最左边还有一个排版按钮,这里主要是为了不方便弹出底部时,可以选择使用弹窗弹出功能栏,功能是同等的,可以选择性阉割。使用某一功能时会有颜色高亮提示。
3. 功能调用

首先要在activity中初始化控件,实现setOnDecorationChangeListener接口,监听点击某个功能按钮时表现对应高亮,让用户看到如今正在所使用的排版格式。
                //输入框显示字体的大小
      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.setOnDecorationChangeListener { _, types ->
            val flagArr = ArrayList<String>()
            for (i in types.indices) {
                flagArr.add(types.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)
            }
      }

插入图片和视频:本来是调用特有的方法实现,但是可定制化不高,我就改成了插入html的形式
public void insertVideoPercentage(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 + "');");
    }
public void insertImage(String url, String alt) {
      exec("javascript:RE.prepareInsert();");
      String testStr = "<img src=\"" + url + "\" alt=\"" + alt + "\" width=\"80%\"><br><br>";
      exec("javascript:RE.insertHTML('" + testStr + "');");
    }
        // 以下图片视频地址,来自网络素材
    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)

    }
图片删除:插入图片后点击图片可将图片删除:
@SuppressLint("ClickableViewAccessibility")
    public void setImageClickListener(ImageClickListener imageClickListener) {
      this.imageClickListener = imageClickListener;
      if (this.imageClickListener != null) {

            RichEditor.this.setOnTouchListener(new OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                  switch (event.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                            DownX = (int) event.getX();
                            DownY = (int) event.getY();
                            moveX = 0;
                            moveY = 0;
                            currentMS = System.currentTimeMillis();//long currentMS   获取系统时间
                            break;


                        case MotionEvent.ACTION_MOVE:
                            moveX += Math.abs(event.getX() - DownX);//X轴距离
                            moveY += Math.abs(event.getY() - DownY);//y轴距离
                            DownX = event.getX();
                            DownY = event.getY();
                            break;

                        case MotionEvent.ACTION_UP:
                            long moveTime = System.currentTimeMillis() - currentMS;//移动时间
                            //判断是否继续传递信号
                            if (moveTime < 400 && (moveX < 25 && moveY < 25)) {
                              //这里是点击
                              HitTestResult mResult = getHitTestResult();
                              if (mResult != null) {
                                    final int 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(new Runnable() {
                                          @Override
                                          public void run() {
                                                if (imageClickListener != null) {
                                                    if (imageUrl.contains("file://")) {
                                                      //说明是本地文件去除
                                                      String newImageUrl = imageUrl.replace("file://", "");
                                                      imageClickListener.onImageClick(newImageUrl);
                                                    } else {
                                                      imageClickListener.onImageClick(imageUrl);
                                                    }

                                                }
                                          }
                                        }, 200);

                                    } else {
                                        //不是点击的图片
                                    }
                              }
                            }

                            break;
                  }
                  return false;
                }
            });

      }
    }
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, "")
            currentUrl = ""
            rich_Editor.html = newUrl
            if (RichUtils.isEmpty(rich_Editor.html)) {
                rich_Editor.html = ""
            }
            popupWindow?.dismiss()
      }
插入链接:必须要先执行一次restorerange ,控制用户选择的文本范围或光标的当前位置
RE.restorerange = function(){
    var selection = window.getSelection();
    selection.removeAllRanges();
    var range = document.createRange();
    range.setStart(RE.currentSelection.startContainer, RE.currentSelection.startOffset);
    range.setEnd(RE.currentSelection.endContainer, RE.currentSelection.endOffset);
    selection.addRange(range);
}

RE.insertLink = function(url, title) {
    RE.restorerange();
    var sel = document.getSelection();
    if (sel.toString().length == 0) {
      document.execCommand("insertHTML",false,"<a href='"+url+"'>"+title+"</a>");
    } else if (sel.rangeCount) {
       var el = document.createElement("a");
       el.setAttribute("href", url);
       el.setAttribute("title", title);

       var range = sel.getRangeAt(0).cloneRange();
       range.surroundContents(el);
       sel.removeAllRanges();
       sel.addRange(range);
   }
    RE.callback();
}
核心的处置惩罚:
(1) 每一次调用排版功能都要调用一次,重新处置惩罚输入核心,避免核心冲突
private fun againEdit() {
      //如果第一次点击例如加粗,没有焦点时,获取焦点并弹出软键盘
      rich_Editor.focusEditor()
      KeyBoardUtils.openKeyboard(edit_name, this@RichTextActivity)
    }
public void focusEditor() {
      requestFocus();
      exec("javascript:RE.focus();");
    }
RE.focus = function() {
    var range = document.createRange();
    range.selectNodeContents(RE.editor);
    range.collapse(false);
//    var selection = window.getSelection();
//    selection.removeAllRanges();
//    selection.addRange(range);
    RE.editor.focus();
}
(2)这里弹出排版页面时必须同时弹出键盘,否则输入框的核心消失,之前所设置的格式也会随之消失。好比我在底部点了个加粗结果,再点击排版弹窗时,加粗结果应该存在。但是一旦键盘关闭核心消失,光标的结果也会被重置。
https://img-blog.csdnimg.cn/direct/35a556652d934af2aaa8de8d25e18799.png#pic_center
        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))
            }
      }
主要的逻辑代码(RichTextActivity.kt):
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.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? ->
            //删除图片
            val removeUrl =
                "<img src=\"$currentUrl\" alt=\"dachshund\" width=\"80%\"><br>"
            val newUrl: String = rich_Editor.html.replace(removeUrl, "")
            currentUrl = ""
            rich_Editor.html = newUrl
            if (RichUtils.isEmpty(rich_Editor.html)) {
                rich_Editor.html = ""
            }
            popupWindow?.dismiss()
      }
      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() {
      //如果第一次点击例如加粗,没有焦点时,获取焦点并弹出软键盘
      rich_Editor.focusEditor()
      KeyBoardUtils.openKeyboard(edit_name, this@RichTextActivity)
    }
      override fun onActionPerform(type: ActionType?, vararg values: Any?) {      var value = ""      if (values != null && values.isNotEmpty()) {            value = values 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企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【Android】基于webView打造富文本编辑器(H5)