热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

AndroidTextView预渲染研究

AndroidTextView预渲染研究-Android中的TextView是整个framework中最复杂的控件之一,负责Android中显示文本的大部分工作,framwork中

Android中的TextView是整个framework中最复杂的控件之一,负责Android中显示文本的大部分工作,framwork中的许多控件也直接或者间接的继承于TextView,例如Button,EditText等。其内部实现也相当复杂,单论代码行数来说,android-22中TextView有足足9509行,另外,TextView中许多操作都非常繁重,例如setText操作,需要设置SpanWatcher,或者需要重现创建一个SpannableString,还需要根据情况重新创建Text Layout,这些操作加起来之后令一次setText操作非常耗时。为了提升TextView的渲染效率,最近研究了一下预渲染的方法,接下来给大家讲解一下原理。

TextView渲染基本原理

首先来介绍下TextView的基本渲染原理,总的来说,TextView中负责渲染文字的主要是这三个类:

BoringLayout

主要负责显示单行文本,并提供了isBoring方法来判断是否满足单行文本的条件。

DynamicLayout

当文本为Spannable的时候,TextView就会使用它来负责文本的显示,在内部设置了SpanWatcher,当检测到span改变的时候,会进行reflow,重新计算布局。

StaticLayout

当文本为非单行文本,且非Spannable的时候,就会使用StaticLayout,内部并不会监听span的变化,因此效率上会比DynamicLayout高,只需一次布局的创建即可,但其实内部也能显示SpannableString,只是不能在span变化之后重新进行布局而已。

另外,以上三个类都继承于Layout类,在此类中统一负责文本的具体绘制,在Layout.draw方法中,会对文本一行一行的进行渲染:

TextLine tl = TextLine.obtain();
 
// Draw the lines, one at a time.
// The baseline is the top of the following line minus the current line's descent.
for (int i = firstLine; i <= lastLine; i++) {
   ....
   Directions directiOns= getLineDirections(i);
   if (directiOns== DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTabOrEmoji) {
     // XXX: assumes there's nothing additional to be done
     canvas.drawText(buf, start, end, x, lbaseline, paint);
   } else {
     tl.set(paint, buf, start, end, dir, directions, hasTabOrEmoji, tabStops);
     tl.draw(canvas, x, ltop, lbaseline, lbottom);
   }
}
TextLine.recycle(tl);

可以看出来对于Spannble,或者包含emoji的文本的话,实际渲染操作是交给了TextLine去绘制,否则直接使用canvas.drawText,TextLine负责单行复杂文本的绘制,其中Spannable, Emoji之类的绘制逻辑都包含在里面,TextLine的绘制逻辑也并非十分高效,这里后续将会继续说明其应该如何优化。

TextLayoutCache

Canvas在drawText的时候,如果需要每次都计算字体的大小,边距等之类的话,就会非常耗时,导致drawText时间会拉的很长,为了提高效率,android在4.0之后引入了TextLayoutCache,使用LRU Cache缓存了字形,边距等数据,提升了drawText的速度,在4.4中,这个cache的大小是0.5M,全局使用,并且会在Activity的configurationChanged, onResume, lowMemory, updateVisibility等时机,会调用Canvas.freeTextLayoutCache来释放这部分内存。由于这部分的cache是系统底层控制的,我们无法做具体的控制。

TextView的预渲染优化

从TextView的渲染原理来看,如果只是单纯的显示文本,我们根本不需要另外设置SpanWatcher来监听span的变化,因此我们可以直接使用BoringLayout或者StaticLayout来直接显示文本内容,但是BoringLayout只能显示单行文本,因此这里最好的选择是直接用StaticLayout

我们选择了自定义View,并希望最终有这样的一个接口:

public class StaticLayoutView extends View {
  private Layout layout = null;
  public void setLayout(Layout layout) {
    this.layout = layout;
    requestLayout();
  } 
  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
 
    canvas.save();
    if (layout != null) {
      layout.draw(canvas, null, null, 0);
    }
    canvas.restore();
  }
}

我们可以直接通过设置这个view的Layout来绘制文本,并在onDraw方法中直接使用这个Layout对象来绘制文本。在这里我们摒弃了setText方法,直接通过Layout来绘制文本,而这里的Layout对象,我们可以通过预先创建之后才设置进去(这里可以放到单独的一个线程中创建),这样对比起普通TextView的setText方法,我们减少了setText中的许多消耗,可以大幅度的提升效率。

StaticLayout的创建非常简单,只需要给定文本,宽度等就能直接创建。另外,为了预先填充TextLayoutCache,我们也可以在创建完StaticLayout对象之后,预先在一个dummy canvas中draw出来:

StaticLayout layout = new StaticLayout(TestSpan.getSpanString(i), textPaint, hardCodeWidth, alignment, 1.0f, 0f, true);
layout.draw(dummyCanvas);

性能对比

接下来我们测试一下具体的性能,这里的testcase放到了Github上:StaticLayoutView

testcase的内容为,在一个ListView中,显示300个Item,每个item都是一段纯文本,里面全都是包含有大量ImageSpan的SpannableString,进行两边的对比,一边是直接使用StaticLayout,一边是使用普通的TextView,并且这300段文本不全相同,长度不同,随机生成,在StaticLayout的testcase中,StaticLayout都是预先在另外一个线程创建好之后才设置进去的,另外SpannableString也是预先生成好的。

另外,在这里为了模拟真实app繁重的后台工作,另外创建了3个线程,不停在做浮点预算以尝试抢占CPU资源。

测量性能的指标为,ListView连续向下滚动,测量其平均帧率为多少,分别测量五次,计算其平均值,最终性能测试结果如下:

这里测试的机器是MX3,左侧是直接使用StaticLayout的方案,右侧是系统的默认方案,Y轴是FPS,可以看出来,使用优化之后的方案,帧率提升了许多。

References

Improving Comment Rendering on Android

这篇文章介绍了Instagram如何优化他们的TextView渲染的效率,这也是这里优化方法的来源,Instagram也是直接使用StaticLayout并通过预先创建Layout的方法来减少了ListView滚动过程中的掉帧率,并且效果非常显著。这篇文章算是给出了这里的原理解析以及一个简单的实现

以上就是对Android TextView预渲染 的资料整理,后续继续补充相关资料,谢谢大家对本站的支持!


推荐阅读
  • 本文介绍了如何通过C#语言调用动态链接库(DLL)中的函数来实现IC卡的基本操作,包括初始化设备、设置密码模式、获取设备状态等,并详细展示了将TextBox中的数据写入IC卡的具体实现方法。 ... [详细]
  • 概述Android开发过程中,经常遇到Textview展示不完全的情况。遇到此情况,通常的处理是:方案一Textview添加android:ellipsize属性,让展示不 ... [详细]
  • Android TextView利用measureText自适应文本字体大小宽度
    AndroidTextView利用measureText自适应文本字体大小宽度常常有这种情况,UI设计师限定一个文本TextView的宽度值比如80dip,但是该文本长度 ... [详细]
  • 处理Android EditText中数字输入与parseInt方法
    本文探讨了如何在Android应用中从EditText组件安全地获取并解析用户输入的数字,特别是用于设置端口号的情况。通过示例代码和异常处理策略,展示了有效的方法来避免因非法输入导致的应用崩溃。 ... [详细]
  • 作为一名Android应用开发新手,我在尝试将MediaPlayer处理逻辑从MainActivity分离到另一个类时遇到了问题。尽管搜索了很长时间,但仍未找到满意的解决方案。 ... [详细]
  • Android中怎么利用TextView显示部分文字高亮
    这篇文章将为大家详细讲解有关Android中怎么利用TextView显示部分文字高亮,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇 ... [详细]
  • Android,TextView优雅显示长文本、富文本Android提供了TextView这个类作为Android开发当中展示文字的工作,最近笔者在做类似于一个展示类型的APP,发现TextV ... [详细]
  • 单个跑马灯效果其实只需要把TextView的属性设置成下面即可:<?xmlversion"1.0"encoding"utf-8"?>< ... [详细]
  • 转载至:http:blog.csdn.netlmj623565791articledetails24252901不过对原作进行了相关细节优化,所以才有此文..首先我们看一 ... [详细]
  • TextView属性详解:autoLink设置  是否当文本为URL链接email电话号码map时,文本显示为可点击的链接。可选值(nonewebemailphonema ... [详细]
  • 在Android中实现黑客帝国风格的数字雨效果
    本文将详细介绍如何在Android平台上利用自定义View实现类似《黑客帝国》中的数字雨效果。通过实例代码,我们将探讨如何设置文字颜色、大小,以及如何控制数字下落的速度和间隔。 ... [详细]
  • 本文深入探讨了WPF框架下的数据验证机制,包括内置验证规则的使用、自定义验证规则的实现方法、错误信息的有效展示策略以及验证时机的选择,旨在帮助开发者构建更加健壮和用户友好的应用程序。 ... [详细]
  • td{border:1pxsolid#808080;}参考:和FMX相关的类(表)TFmxObjectIFreeNotification ... [详细]
  • android 中 TextView.setText(CharSequence text, BufferType type)的了解
    我们经常用TextView.setText(“”);的方法,但是TextView有一个另外一个俩个入参,之前不太知道其用法,现在记录一下效果图image.png核心代码***实现同 ... [详细]
  • 如何在Android中利用TextView对字体颜色进行更改?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希 ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有