Android StaticLayout运用于自定义View

原创
03/02 14:08
阅读数 150

一、TextView文本布局

TextView是Android系统中设计最复杂的View组件之一,很多View组件都继承自TextView,如Button、 EditText、DigitalClock、CheckedTextView等。其中EditText的字符输入和光标都是继承TextView,可以说TextView完全可以替代EditTex。

TextView文本布局中存在三种类型:

  • BoringLayout: 只能处理单行从左到右的文本字符串,不支持Spannable或者Html
  • StaticLayout: 可处理多行文本文本,支持从左到右,从右到左等文本字符串,支持Spannable和Html
  • DynamicLayout: 可处理多行动态文本输入,支持从左到右和葱油到左的文本字符串,如Editor相关操作,支持Spannable和Html

以上三种文本布局功能非常重合,TextView源码中也并不是因为单行文本就会选用BoringLayout,创建文本布局时优先判断是否存在Spannable文案,如果存在则使用DynamicLayout,最后保底方案是StaticLayout,如果不存在,紧接着判断是否可以使用BoringLayout处理。

我们想象中的应该是 BoringLayout->StaticLayout->DynamicLayout ,但情况恰恰相反,StaticLayout永远是最末尾的选择,为什么会出现这种情况呢?

可能因素如下:

DynamicLayout内部本身通过StaticLayout委托实现了进行文本测量,因此可以能原因就是为了使得TextView更好扩展。

 

二、使用StaticLayout自定义View

StaticLayout 是一个文本测量工具,并不是View的子类,但因为提供Layout能力,因此,我们可以知道,和绘制普通View不一样的是,我们刷新View的需要调用requestLayout+invalidate() ,rquestLayout虽然大概率能出发invalidate,但有些情况可能不会,你次invalidate是必要的。

public class StaticTextView extends View {

    private TextPaint mTextPaint;
    private DisplayMetrics mDisplayMetrics;
    private int mContentHeight = 0;
    private int mContentWidth = 0;

    private StaticLayout staticLayout;

    public StaticTextView(Context context) {
        this(context, null);
    }

    public StaticTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public StaticTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
       // ((ViewGroup)getParent()).setLayerType(View.LAYER_TYPE_SOFTWARE,null);
        initPaint();
        String text="在Android开发中,Canvas.drawText不会换行,即使一个很长的字符串也只会显示一行,超出部分会隐藏在屏幕之外.StaticLayout是android中处理文字的一个工具类,StaticLayout 处理了文字换行的问题";
        setText(text);

    }

    private void initPaint() {
        mDisplayMetrics = getResources().getDisplayMetrics();
        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG|Paint.DITHER_FLAG);
        mTextPaint.setAntiAlias(true);
        mTextPaint.setStyle(Paint.Style.STROKE);
        mTextPaint.setTextSize(sp2px(20));

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

        if (widthMode != MeasureSpec.EXACTLY) {
            widthSize = mDisplayMetrics.widthPixels / 2;
        }

        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        if (heightMode != MeasureSpec.EXACTLY) {
            heightSize =  staticLayout!=null?staticLayout.getHeight():0;
        }

        setMeasuredDimension(widthSize, heightSize);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mContentHeight = (int) (h - dp2px(4));
        mContentWidth = w;


    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        float strokeWidth = mTextPaint.getStrokeWidth() * 2;
        if (mContentWidth <= strokeWidth || mContentHeight <= strokeWidth) {
            return;
        }

        if(staticLayout!=null){
            staticLayout.draw(canvas);
        }

    }

    public void setText(final  CharSequence text) {
       post(new Runnable() {
           @Override
           public void run() {
               staticLayout = new StaticLayout(text, mTextPaint, getWidth(), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
               requestLayout();
           }
       });
    }

    public float dp2px(float dp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mDisplayMetrics);
    }
    public float sp2px(float dp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, dp, mDisplayMetrics);
    }


}

 

三、TextView性能问题

虽然本文讨论的是StaticLayout用法,但是TextView性能问题也值得关注。TextView存在频繁requestLayout+invalidate问题(除去高度不变+宽度不变的情况,之所以是requestLayout+invalidate,在布局不变的情况下requestLayout触发不了重绘),此外,优先选用DynamicLayout,导致内部存在监控机制,性能上存在一些问题。

 

 

 

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部