文档章节

Android原生简易图文编辑器和展示实现

叶大侠
 叶大侠
发布于 2017/05/21 10:01
字数 1588
阅读 51
收藏 1

talk is cheep, show you the code. 源码参考

背景

尽管Android设备的性能日益增强,但是通过webview来展示内容和原生的体验还是有一定的差距的,在某些情况下,我们只是需要简单的图文并排就够了,比如一些帖子,这个时候用webview就显的有点重,考虑到这一点,我们决定在客户端原生支持特定的网页标签。

为了兼顾到各个平台,我们约定输出是标准的html内容,对于已有的内容,可以进行内容的重新排版,把多余的标签去掉并换成约定好的标签。

设计思路

笔者有着多年对于markdown编辑器的使用经验,对于markdown语法的简洁有深度的喜爱,对于很多时候的编辑工作都是够用了,我更倾向于轻便够用而非周全复杂的东西,在设计编辑器的时候,不经意就想到了markdown。

经过协商我们初期先支持下面的几种简单的样式:

  1. 标题[一级] 对应 <h1> 标签。
  2. 文本段落[一级] 对应 <p> 标签。
  3. 文本加粗、换行[二级,嵌在<p>里面] 对应 <b> <br> 标签。
  4. 图片[一级],对应 <img> 标签。
  5. 超链接[二级] 对应 <a> 标签。

约定结果:

正确:

<h1>这是一级标题</h1>
<p>段落1</p>
<p>段落2<b>我是加粗部分</b>hello<br></p>
<img src="http://github.com/pic.png" width="100", height="100"/>
<p>段落3<b>加粗加粗加粗<br>加粗加粗加粗</b></p>

错误:

<p>段落1<p>内部段落</p></p>
<p>段落3<img src="http://github.com/pic.png" width="100", height="100"/></p>

定义好支持的标签之后,接着就是由设计师设计好各个标签对应的文字样式,间距和图片的展示方式了。

编辑器实现

通过研究几个开源项目,发现原生实现富文本编辑器主要有两个思路,一个是基于单个EditText通过组合不同的Spannable来实现,另外一个是组合EditText和ImageView等不同的控件,个人认为第二种方式更加灵活,但是加粗,链接等处理也是需要Spannable的,因此组合了两种方式。

根据上面约定支持的标签,我定义了三种类型的控件:

YRichEidtText

  1. EditImageView是插入编辑框的图片控件,它也负责了上传的相关工作。
  2. RichEditText这个控件负责段落的编辑,段落内可以支持一些文字样式,比如加粗和超链接。这个控件是cwac-richedit的一个实现,它封装了很多的spannable实现,这里只是用到了加粗和链接,源码虽然有改动,为了尊重作者的劳动成果,决定不改动它的名字。
  3. HeadingEditText这个控件用来处理标题的输入,其实就是字体大一些和加粗的EditText。

RichTextEditor是比较核心的实现,它继承了ScrollView,它的职责是协调控件和光标、返回键之间的交互,主要实现了下面的接口:

输出:生成html的过程其实就是遍历各个控件了RichEditText里面的Spannable的过程。

Note: 值得一提的是笔者在看的时候,发现cwac-richedit这个项目是运行不起来的,一般情况下到这里就放弃对这个库的研究了,但是翻了下代码,发现作者的单元测试很充分,而且文档描述也算是比较清晰,仔细研究了一下,发现代码设计的有很多亮点,思路也非常清晰,于是后面就选择了这个库作为基础的文字段落样式实现,如果想基于它实现下划线,斜体,字体颜色等功能,应该是非常方便的一件事情。

网页内容显示实现

我们的富文本会作未帖子的详情和评论列表中,由于是出现在列表中,我们需要考虑到控件的复用问题,所以一开始定义一个完整实现的富文本控件的思路就放弃了,而是通过按竖直方向拆分不同的Item,利用RecylerView或者ListView的复用特性来实现,尽管这样做起来会麻烦不少,但是完美地避免了列表不断滑动过程中对象不断创建和销毁带来的内存抖动问题。

从下面流程图可以看出这个处理流程,首先解析html相关节点,并把其中相关的值和属性封装到不同的对象中,然后通过列表数据去驱动整个视图的显示,解析Html是通过Jsoup来实现的,接口非常友好,和用Jquery差不多。

由于要处理的标签很少,在Jsoup的帮助下,整个解析代码不超过30行:


    Document doc = Jsoup.parseBodyFragment(htmlContent);
    List<Node> childNodeList = doc.body().childNodes();
    if (childNodeList == null || childNodeList.isEmpty()) {
        return null;
    }
    final int size = childNodeList.size();
    List<IHtmlElement> elList = new ArrayList<>();
    for (int pos = 0; pos != size; pos++) {
        Node childNode = childNodeList.get(pos);
        String tagName = childNode.nodeName();
        if (tagName.equalsIgnoreCase("h")) {
            elList.add(new PElement(Html.fromHtml(((Element) childNode).html())));
        } else if(tagName.equalsIgnoreCase("h1")){
            elList.add(new HElement(((Element) childNode).html()));
        }else if (tagName.equalsIgnoreCase("img")) {
            String src = childNode.attr("src");
            String width = childNode.attr("width");
            String height = childNode.attr("height");
            elList.add(new ImgElement(src, NumberUtils.parseInt(width, 0), NumberUtils.parseInt(height, 0)));
        } else {
            if (childNode instanceof Element) {
                elList.add(new PElement(Html.fromHtml(((Element) childNode).html())));
            } else {
                elList.add(new PElement(htmlContent));
            }
        }
    }

节点对象和对应的Item视图,可以看到结构还是非常清晰的,对于以后想添加一些其他的样式也是很好扩展的。

总结

虽然不是一个很复杂的东西,从整个实现思路来看,还是比较好的兼顾了性能和可扩展度,也很好体现了分而治之和数据驱动视图的开发模式。不过从功能上来说还是存在一些缺陷的,比如光标不能跨段进行选择, 只能支持至上而下的排版。

参考

  1. XRichText: https://github.com/sendtion/XRichText
  2. cwac-richedit: https://github.com/commonsguy/cwac-richedit
  3. Html解析库Jsoup:https://jsoup.org/

© 著作权归作者所有

共有 人打赏支持
叶大侠

叶大侠

粉丝 57
博文 44
码字总数 67312
作品 5
广州
程序员
java.lang.AssertionError Android WebView

先描述下这个问题板块的功能:长文编辑器,图文混排,图片实现懒加载(直接点就是可以,写一篇几十页的长文章,能够自由插图,滑动加载图片)。实现方式,采用webview,结合javascript、htm...

Sn__
07/11
0
0
微信公众平台如何与Web App结合

Web App简而言之就是为移动平台而优化的网页,它可以表现得和原生应用一样,并且克服了原生应用一些固有的缺点。一般而言Web App最大的入口是浏览器,但现在微信公众平台作为新兴的平台,结合...

BearCatYN
2014/07/02
0
1
移动开发者必须知道的Android框架推荐

一些总结出来的Android快速开发框架,全部都是开源框架,附带项目地址,是开发学习的绝佳资料。 thinkAndroid项目 github地址:https://github.com/white-cat/ThinkAndroid 功 能:ThinkAndr...

程序袁_绪龙
2014/09/02
0
0
【极客之作】快到极致的Android模拟器——Genymotion

转载声明:Ryan的博客文章欢迎您的转载,但在转载的同时,请注明文章的来源出处,不胜感激! :-) http://my.oschina.net/ryanhoo/blog/141824 还在用Android原生模拟器? 给你推荐一款全方位...

RyanHoo
2013/07/02
0
44
Android启动页黑屏及最优解决方案

前言 相信做过Android的朋友都知道,当一个APP启动时,界面会首先展示一个白屏或者黑屏,然后再进入欢迎页,稍作停留最后进入APP主页。那么这个黑屏或者白屏到底是怎么一回事呢?它的最好的解...

猴亮屏
08/10
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

[MicroPython]STM32F407开发板驱动OLED液晶屏

1.实验目的 1.学习在PC机系统中扩展简单I/O 接口的方法。 2.进一步学习编制数据输出程序的设计方法。 3.学习 F407 Micropython开发板控制OLED显示字符。 2.所需元器件 F407 Micropython开发板...

bodasisiter
20分钟前
0
0
php require和include 相对路径一个有趣的坑

以前总是被教育,不要使用相对路径,这样性能比较差,但是相对路径的问题不仅仅是性能哦,看下面这里例子 这是项目结构 .├── main.php├── t│ ├── t1.php│ └── t2.php└─...

anoty
21分钟前
9
0
x64技术之SSDT_Hook

测试环境: 虚拟机: Windows 7 64bit 过PG工具 驱动加载工具 PCHunter64 系统自带的计算器和任务管理器等 实现思路: 实际思路与win32的思路一样.都是替换SSDT表里边的函数地址.不过微软被搞怕...

simpower
22分钟前
0
0
TreeMap源码分析,看了都说好

一、简介 TreeMap最早出现在JDK 1.2中,是 Java 集合框架中比较重要一个的实现。TreeMap 底层基于红黑树实现,可保证在log(n)时间复杂度内完成 containsKey、get、put 和 remove 操作,效率很...

Java小铺
32分钟前
0
0
协变、逆变

概念 假设 A、B表示类型 ≤ 表示继承关系 f<⋅>表示类型转换 若A ≤ B,则 A是B的子类,B是A的超类 协变、逆变 什么是型变?型变(type variance)允许对类型进行子类型转换。 为了下面讲解先...

obaniu
38分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部