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

full courage 2024-07-25 16:03:02 阅读 56

目录

前言一、实现效果二、具体实现1. 导入网页资源2. 页面设计3. 功能调用4. 完整代码

总结


前言

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定义了支持的排版格式。

<code>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">code>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">code>

<link rel="stylesheet" type="text/css" href="normalize.css">code>

<link rel="stylesheet" type="text/css" href="style.css">code>

</head>

<body>

<div id="editor" contenteditable="true"></div>code>

<script type="text/javascript" src="rich_editor.js"></script>code>

</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"code>

android:layout_width="match_parent"code>

android:layout_height="0dp"code>

android:layout_weight="1"code>

android:overScrollMode="never" />code>

主要样式如下:

在这里插入图片描述

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

3. 功能调用

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

<code>//输入框显示字体的大小

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"code>

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"code>

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>");code>

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

在这里插入图片描述

<code>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=""code>

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"code>

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"code>

againEdit()

rich_Editor.insertVideoPercentage(path, "80%","30%")

KeyBoardUtils.openKeyboard(edit_name, this@RichTextActivity)

}

}

4. 完整代码

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


总结

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



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。