美文网首页
自定义View之变色文字

自定义View之变色文字

作者: Noblel | 来源:发表于2017-12-13 17:33 被阅读0次

需求来源

内涵段子

上面的滑动字体变色实现是怎么做的的呢?

实现思路

1.继承自View还是TextView?
由于继承自TextView我们不需要测量宽高,少做很多处理。所以我们选择TextView

2.实现两种颜色的TextView

2.1. TextView两种颜色,红色和黑色,根据当前进度显示左右不同的颜色
2.2. 在onDraw()中利用计算好的位置分割然后绘制文字,裁剪绘制部分即可。
2.3. 使用canvas.drawText(text, x, y, paint);绘制文字

具体实现

先获取自定义属性:

private void initPaint(Context context, AttributeSet attrs) {
    //获取自定义属性
    TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ColorTrackTextView);
    //获取默认色
    int originColor = array.getColor(R.styleable.ColorTrackTextView_originColor, getTextColors().getDefaultColor());
    //获取变化的颜色
    int changeColor = array.getColor(R.styleable.ColorTrackTextView_changeColor, getTextColors().getDefaultColor());
    mOriginPaint = getPaintByColor(originColor);
    mChangePaint = getPaintByColor(changeColor);
    //记得回收,为什么要回收?稍后做解释
    array.recycle();
}

绘制不同颜色

@Override
protected void onDraw(Canvas canvas) {
    //获取变色进度
    int middle = (int) (mCurrentProgress * getWidth());
    if (mDirection == Direction.LEFT_TO_RIGHT) {//左边是红色右边是黑色
        //绘制变色,从0-middle为变化色,后面为默认色
        drawText(canvas, mChangePaint, 0, middle);
        drawText(canvas, mOriginPaint, middle, getWidth());
    } else {
        drawText(canvas, mChangePaint, getWidth() - middle, getWidth());
        drawText(canvas, mOriginPaint, 0, getWidth() - middle);
    }
}

private void drawText(Canvas canvas, Paint paint, int start, int end) {
    //canvas的save方法作用后面做解释
    canvas.save();
    
    //剪辑矩形,截取绘制的内容
    Rect rect = new Rect(start, 0, end, getHeight());
    canvas.clipRect(rect);
    
    String text = getText().toString();
    
    Rect bounds = new Rect();
    //获取文字的范围
    paint.getTextBounds(text, 0, text.length(), bounds);
    //获取字体的宽度
    int x = getWidth() / 2 - bounds.width() / 2;
    Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();
    //文字高度的一半到基线的距离
    //top表示基线到文字最上面的位置的距离 是个负值 bottom为基线到最下面的距离,为正值
    int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
    //基线
    int baseLine = getHeight() / 2 + dy;
    //这里drawText中的x和y是原点坐标,什么是原点坐标看图
    canvas.drawText(text, x, baseLine, paint);
    canvas.restore();
}

array.recycle()

TypedArray是获取XML layout的属性值,使用完后要使用recycle()方法将TypedArray回收。TypedArray并没有占用IO,线程,仅仅是一个变量为什么要回收呢?

点击obtainStyledAttributes一直走找到ResourcesImpl的obtainStyledAttributes的这行代码

final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);

看到实现是TypedArray的静态方法

static TypedArray obtain(Resources res, int len) {
    //从ArrayPool中获取的
    TypedArray attrs = res.mTypedArrayPool.acquire();
    if (attrs == null) {
        attrs = new TypedArray(res);
    }
    ......
}

程序在运行时维护了一个 TypedArray的池,程序调用时,会向该池中请求一个实例,用完之后,调用 recycle() 方法来释放该实例,从而使其可被其他模块复用。

那为什么要使用这种使用池+单例的模式?由于array会随着Activity创建而创建,因此,需要系统频繁的创建array,对内存和性能是一个不小的开销,如果不使用池模式,每次都让GC来回收,很可能就会造成OutOfMemory。

canvas.save()和canvas.restore()

sava()方法:
将当前的matrix和clip保存到一个私有堆栈中。后续调用translate,scale,rotate,skew,concat或clipRect clipPath都将照常,但调用restore(),这些将被重置,save()之前设置将被恢复。

restore()方法:
这个调用平衡了之前的save()调用,并用于删除自上次保存调用以来对matrix /clip状态的所有修改。调用restore()不能比调用save()次数更多

我个人理解的是相当于打个标记,类似于还原点。

自定义View源码

变色字体源码

到此为止我们就可以看到这样的效果

效果图

注意:
onDraw中尽量不要new对象,因为要频繁调用。

相关文章

网友评论

      本文标题:自定义View之变色文字

      本文链接:https://www.haomeiwen.com/subject/tudzixtx.html