Android TextView使用及性能优化

TextView 是Android中最常用的控件,在这里记录下TextView 的用法;

字体

系统自带字体

在Android中可以使用系统自带的4种字体:

  1. normal 普通字体,系统默认使用的字体
  2. sans 非衬线字体
  3. serif 衬线字体
  4. monospace 等宽字体

在XML中使用android:typeface="normal"进行设置

自定义字体

将字体文件放到main/assets/fonts目录下,使用Asset读取字体后进行设置

AssetManager assetManager = mContext.getAssets();Typeface typeface = Typeface.createFromAsset(assetManager, "fonts/SourceCodePro-Bold.otf");textView.setTypeface(typeface);

Drawable

使用android:drawableLeft="@mipmap/ic_launcher"可以设置一张图片显示在文字的上下左右,减少布局层级

Span

使用Span能够在一段TextView中设置不同颜色的字体,链接,图片等内容

ClickableSpan

使用ClickableSpan 能够设置一段文字的点击事件

创建自己的MyClickableSpan:

public class MyClickableSpan extends ClickableSpan {    @Override    public void onClick(View view) {        //点击的回调    }    @Override    public void updateDrawState(TextPaint ds) {        super.updateDrawState(ds);        //可以在这里通过ds来设置可点击文字的样式        ds.setColor(Color.RED);        ds.setUnderlineText(false);    }}

之后使用SpannableStringBuilder来创建字符串,并使用setSpan来为字符串的一部分设置Span对象

SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder("This Is Test Text, Click This");spannableStringBuilder.setSpan(    new MyClickableSpan(),      //span对象    0, 5,                       //开始和结束字符index    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);//标识mTextView.setMovementMethod(LinkMovementMethod.getInstance());  //不设置无法响应点击事件mTextView.setText(spannableStringBuilder);

其中setSpan()方法的最后一个参数标识有以下常量,这些常量标识着在对SpannableStringBuilder进行insert时添加的字符适用的规则:

Spanned.SPAN_EXCLUSIVE_EXCLUSIVE

Spanned.SPAN_EXCLUSIVE_INCLUSIVE

Spanned.SPAN_INCLUSIVE_EXCLUSIVE

Spanned.SPAN_INCLUSIVE_INCLUSIVE

前一个EXCLUSIVE/INCLUSIVE标识着在设置了Span的一段字符之前(紧挨着)插入字符时,被不被包含到Span范围中,EXCLUSIVE表示包含,INCLUSIVE表示不包含;

第二个EXCLUSIVE/INCLUSIVE同理表示插入这段字符之后的效果;

ImageSpan

ImageSpan用于在TextView中插入图片,可以用来实现图文混排

使用方法:

SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder("This Is Test Text, Click This");//创建Span对象ImageSpan imageSpan = new ImageSpan(this, BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));//设置ImageSpan,在第3和第5个字符中间的字符将会被替换成图片spannableStringBuilder.setSpan(imageSpan, 3, 5, Spanned.SPAN_INCLUSIVE_INCLUSIVE);mTextView.setText(spannableStringBuilder);

这样实现的效果是文字与图片底部进行对齐,如果需要图片中线与文字中线对其,需要自己重写ImageSpan

TextView性能优化

介绍:

​ Android 中的TextView中存在着很多EditText中的特性,在setText()方法中会涉及到很多Span相关的操作,比如设置TextWatcher,重新构造Spannable等操作,在我们仅仅显示静态文本的时候这些操作都是没有必要的(通过使用普通的TextView进行Debug来验证普通的TextView的确是Span的);

​ 在大量显示静态文本的时候就可以通过StaticLayout来计算出TextView的布局信息,这项工作可以放到非UI线程来进行,能够减少在setText()的时候UI线程的耗时,达到优化TextView性能的目的;

​ StaticLayout是TextView中用于显示多行静态文本的Layout,也是能够支持SpannableString的,只是不能在Span变化之后重新Layout,所以在大部分场景下已经适用;

使用方法:

  1. 自定义View
public class StaticLayoutView extends View {        private Layout layout = null;        private int width;    private int height;        public void setLayout(Layout layout) {        this.layout = layout;        if (this.layout.getWidth() != width || this.layout.getHeight() != height) {            width = this.layout.getWidth();            height = this.layout.getHeight();            requestLayout();        }    }     @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);                canvas.save();        if (layout != null) {            //直接使用layout来绘制文本            layout.draw(canvas, null, null, 0);        }        canvas.restore();    }    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        super.onLayout(changed, left, top, right, bottom);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        if (layout != null) {            setMeasuredDimension(layout.getWidth(), layout.getHeight());        } else {            super.onMeasure(widthMeasureSpec, heightMeasureSpec);           }            }}

通过这个自定义的View来显示Text,在onDraw()的时候直接使用layout来进行绘制,而设置需要显示的文本则直接使用setLayout()来实现

  1. 创建StaticLayout

            StaticLayout layout = new StaticLayout(                text,            //文本                textPaint,       //TextPaint对象                layoutWidth, //layout宽度,多行的情况下取父容器宽度减去padding即可                Layout.Alignment.ALIGN_NORMAL, //对其方式                1.0f, //间隔倍数,表示1.0倍字体大小                0f,   //增加的间隔                true        );
  2. 在Android 4.0之后增加了TextLayoutCache对文本的测量和渲染结果进行缓存,于是可以在创建了StaticLayout对象之后就调用其draw()方法来给缓存增加本次的测量结果

            Canvas canvas = new Canvas();        staticLayout.draw(canvas);

    这些操作都应该放到非UI线程来完成,比如在文本下载完成之后就生成对应的StaticLayout,之后在TextView要显示的时候setLayout()即可

性能测试

使用下面给出的参考链接中的测试Demo在 ZTE A2017 Android7.1.1 高通820设备上,普通TextView在ListView中连续滚动的帧数是55帧,使用StaticLayout的结果为60帧

可以作为在APP使用CPU资源较多的情况下的优化手段

参考链接:TextView预渲染研究

Android P中的TextView新特性

在Android中,TextView的测量消耗了大量的时间,Android P中提供了PrecomputedText能够将测量这个过程放到后台来执行,减轻对于UI线程的卡顿;

使用方法:

非Android P时,使用AppCompatTextView控件,使用setTextFeature()方法来将文本的measure过程放到其他线程来执行,而不是直接将text应用于TextView;

在调用了这个方法之后如果对TextView进行边距,文字大小等的设置都将会报错;

        textView.setTextFuture(                PrecomputedTextCompat.getTextFuture(                    text,  //文本                    textView.getTextMetricsParamsCompat(), //PrecomputedTextCompat.Params                    executor) //线程池,        );

Prefetch Text Layout in RecyclerView

PrecomputedTextCompat

性能测试

在ListView中仅替换设置Text的方法时未测试出性能与普通方法有什么优势,猜测是ListView没有在getView和显示之间预留时间,

测试项目地址:

https://github.com/GavynZhang/PrecomuptedTextViewTest

更多相关文章

  1. Linux C++工程师2小时了解Android记录
  2. 阿里云物联网平,Android台接入问题(couldn't find "libcoap.so")
  3. 使用Android(安卓)BindingAdapter与InverseBindingAdapter实现Se
  4. android webview 软键盘覆盖 输入框的 问题
  5. Android发邮件
  6. Android四大布局之表格布局行列位置控制
  7. 箭头函数的基础使用
  8. NPM 和webpack 的基础使用
  9. Python list sort方法的具体使用

随机推荐

  1. Android(安卓)SDK下载用的代理
  2. android 控件属性大全
  3. Android入门介绍
  4. Android平板大屏幕的设置
  5. android的五大布局
  6. Android设置TextView的行间距、行高。
  7. Android编程: 调试方法
  8. Android起步--简单的乘法
  9. android system setup and building (1)
  10. Android应用开发相关下载资源(2014/12/14