前言
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界说了支持的排版格式。
- 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" />
复制代码 主要样式如下:
除了底部的功能栏可以使用,最左边还有一个排版按钮,这里主要是为了不方便弹出底部时,可以选择使用弹窗弹出功能栏,功能是同等的,可以选择性阉割。使用某一功能时会有颜色高亮提示。
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[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)
- }
- }
复制代码 插入图片和视频:本来是调用特有的方法实现,但是可定制化不高,我就改成了插入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)这里弹出排版页面时必须同时弹出键盘,否则输入框的核心消失,之前所设置的格式也会随之消失。好比我在底部点了个加粗结果,再点击排版弹窗时,加粗结果应该存在。但是一旦键盘关闭核心消失,光标的结果也会被重置。

- 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[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? ->
- //删除图片
- 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[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企服之家,中国第一个企服评测及商务社交产业平台。 |