本编辑器基于静态html实现了客户端编辑器所需要的几乎全部功能及其实际流程所需,包括下载,视频缩略图,自定义标签样式 自定义点击事件本地资源转网络资源需要改为你自己的服务器)等等
下面一个实际项目的应用演示
例子:apk
Add it in your root build.gradle at the end of repositories:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
Step 2. Add the dependency
dependencies {
implementation 'com.github.RexSuper:RichEditor:1.0.5'
}
下面的示例代码必须要看,因为富文本有些功能后续不得不自己填充,比如用户的资源文件最终得放到自己服务器生成链接,视频缩略图等拓展功能,编辑模式和再现的模式也是不同的,务必看演示案例
2019-09-04 优化用户体验,在编辑时候所有资源改为本地,加载转在线改为,最后统一发布的时候
2019-08-20 增加音频功能 增加链接下载功能 和视频下载功能
2019-08-16 增加各种文件功能
2019-08-15 增加添加视频功能
2019-06-21 增加基本图文混排等功能
该项目采用的方法是,放静态网页到项目中,通过webview js和html交互的方式,调用html自带的各种富文本编辑的功能,并加上了一些细节性需求优化,提供一定的拓展性,尝试用过用原生实现,安卓自带的Html类里面自定义标签,但是非常难实现和再现,再和其他端打通非常困难,最后发现直接用html编辑是最好的选择,可直接生成html源代码,也可以直接设置显示,这个方法需要你知道一定的js和webview前端知识
最后特别感谢wasabeef/richeditor-android,因为里面的RichEditor提供了给我全新的思路,来实现这些需求,让我越过了的原生实现的种种困难, 另外感谢HTML"Mogul" @ZX 让我知道html这些细节坑点 如何实现 ,可以视为它的RichEditor升级补充版
---> | 支持功能 | Support function | <--- |
---|---|---|---|
Video | Image | Audio | File And Download |
Blod | Italic | Subscript | Strikethrough |
Underline | JustifyLeft | JustifyCenter | JustifyRight |
Blockquote | Heading | Undo | Redo |
Indent | Outdent | InsertLink | Checkbox |
TextColor | TextBackgroundColor | FontSize | UnorderedList |
OrderedList | Hint | Superscript | InsertLink |
#Edit Demo
package com.rex.richeditor;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.rex.editor.common.EssFile;
import com.rex.editor.view.RichEditorNew;
import com.rex.richeditor.tools.HttpFakeUtils;
import com.rex.editor.common.FilesUtils;
import java.util.List;
/**
* 主要用于演示 视频 音频 文件 插入 及其后续实际流程
*/
public class ActualDemoActivity extends AppCompatActivity {
public final static int RESULT_CHOOSE = 123;
private RichEditorNew richEditor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_actual_demo);
richEditor = findViewById(R.id.richEditor);
//自动为视频添加缩略图
richEditor.setNeedAutoPosterUrl(true);
richEditor.focusEditor();
//本地文件上传到服务器生成在线文件,这一步如果需要发布到服务器,是必不可少的
//为了优化体验,我们将这一步放到最后,将所有本地src或者href,一起上传,避免在编辑的时候 选择一次等待上传一次
//后续也可以做后台上传,进一步增加你的app的用户体验
//1.用户编辑插入各种文件 src是本地文件
//2.最后发布的时候将所有src提取出来,一起上传
//3.替换掉html中本地的src
//4.将html code存到服务器
}
private String orginHtml = "";
private int index = 0;
private int max = 0;
public void publish(View view) {
index = 0;
orginHtml = richEditor.getHtml();
List<String> allSrcAndHref = richEditor.getAllSrcAndHref();
if (allSrcAndHref == null || allSrcAndHref.size() == 0) {
//没有资源文件直接发布
publishArticle(orginHtml);
} else {
max = allSrcAndHref.size();
Log.i("rex", "上传进度:" + index + "/" + max);
for (String src : allSrcAndHref) {
final String orginSrc = src;
HttpFakeUtils.postFile(new EssFile(src), new HttpFakeUtils.HttpResult() {
@Override
public void onSuccess(String url) {
orginHtml = orginHtml.replace(orginSrc, url);
checkStatus(orginHtml);
}
private void checkStatus(String httpHtml) {
++index;
//失败也算进度
Log.i("rex", "上传进度:" + index + "/" + max);
if (index >= max) {
Log.i("rex", "上传结束:" + index + "/" + max);
publishArticle(httpHtml);
}
}
@Override
public void onError(String error) {
checkStatus(orginHtml);
}
});
}
}
}
private void publishArticle(final String httpHtml) {
HttpFakeUtils.publishArticle(httpHtml, new HttpFakeUtils.HttpResult() {
@Override
public void onSuccess(String msg) {
//缩略图功能会产生本地文件可调用此方法清理
richEditor.clearLocalRichEditorCache();
Toast.makeText(ActualDemoActivity.this, msg, Toast.LENGTH_SHORT).show();
Intent intent = new Intent(ActualDemoActivity.this, ShowHtmlActivity.class);
intent.putExtra("html", httpHtml);
intent.putExtra("isPublish", true);
startActivity(intent);
}
@Override
public void onError(String error) {
}
});
}
/**
* 这里采用系统自带方法,可替换为你更方便的自定义文件选择器
*/
public void openDirChooseFile(View view) {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);//多选
startActivityForResult(intent, RESULT_CHOOSE);
}
public void chooseFile(Activity activity, int requestCode) {
// String[] mimeTypes = {MimeType.DOC, MimeType.DOCX, MimeType.PDF, MimeType.PPT, MimeType.PPTX, MimeType.XLS, MimeType.XLSX};
// openDirChooseFile(activity, requestCode, mimeTypes);
}
/**
* 根据返回选择的文件,来进行上传操作
**/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && requestCode == RESULT_CHOOSE) {
Uri uri = data.getData();
if (uri != null) {
String abUrl = FilesUtils.getPath(ActualDemoActivity.this, uri);
Log.i("rex", "abUrl:" + abUrl);
EssFile essFile = new EssFile(abUrl);
if (essFile.isImage() || essFile.isGif()) {
richEditor.insertImage(essFile.getAbsolutePath());
} else if (essFile.isVideo()) {
richEditor.insertVideo(essFile.getAbsolutePath());
} else if (essFile.isAudio()) {
richEditor.insertAudio(essFile.getAbsolutePath());
} else {
richEditor.insertFileWithDown(essFile.getAbsolutePath(), "文件:" + essFile.getName());
}
}
}
super.onActivityResult(requestCode, resultCode, data);
}
}
#Show
RichEditorNew richEditor = findViewById(R.id.richEditor);
TextView tvHtmlCode = findViewById(R.id.tvHtmlCode);
if (isPublish) {
tvHtmlCode.setVisibility(View.GONE);
richEditor.setVisibility(View.VISIBLE);
//为图片加上点击事件 Js
//不可编辑 预览模式
richEditor.loadRichEditorCode(html);
richEditor.setOnClickImageTagListener(new RichEditor.OnClickImageTagListener() {
@Override
public void onClick(String url) {
Toast.makeText(ShowHtmlActivity.this, "url:" + url, Toast.LENGTH_LONG).show();
}
});
richEditor.setDownloadListener(DownloadTask.getDefaultDownloadListener(this));
} else {
tvHtmlCode.setText(html);
tvHtmlCode.setVisibility(View.VISIBLE);
richEditor.setVisibility(View.GONE);
}
疑难细节问题记录:https://github.com/RexSuper/RichEditor/wiki/富文本编辑器-特殊记录