废话留在后面直接上代码
public class LinearGradientFontSpan extends ReplacementSpan {
private int mSize;
private int startColor;
private int endColor;
public LinearGradientFontSpan(int startColor, int endColor) {
this.startColor = startColor;
this.endColor = endColor;
}
@Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
mSize = (int) (paint.measureText(text, start, end));
//这段不可以去掉,字体高度没设置,可能出现draw()方法没有被调用,如果你调用SpannableStringBuilder后append一个字符串,效果也是会出来,下面这段就不需要了
// 原因详见https://stackoverflow.com/questions/20069537/replacementspans-draw-method-isnt-called
Paint.FontMetricsInt metrics = paint.getFontMetricsInt();
if (fm != null) {
fm.top = metrics.top;
fm.ascent = metrics.ascent;
fm.descent = metrics.descent;
fm.bottom = metrics.bottom;
}
return mSize;
}
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
LinearGradient lg = new LinearGradient(0, 0, 0, paint.descent() - paint.ascent(),
startColor,
endColor,
Shader.TileMode.REPEAT); //从上到下渐变
paint.setShader(lg);
canvas.drawText(text, start, end, x, y, paint);//绘制文字
}
}
public class MainActivity extends AppCompatActivity {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text2);
textView.setText(getRadiusGradientSpan(
"这个世界\n" +
"\n并没有我们看上去那么简单\n" +
"\n人各有命,上天注定\n" +
"\n有人天生为王,有人落草为寇\n" +
"\n脚下的路如果不是你自己的选择\n" +
"\n那旅程的终点在哪也没人知道\n" +
"\n你会走到哪,会碰到谁,都不一定\n", 0xFF000000, 0xFFFF0000));
}
public static SpannableStringBuilder getRadiusGradientSpan(String string, int startColor, int endColor) {
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(string);
LinearGradientFontSpan span = new LinearGradientFontSpan(startColor, endColor);
spannableStringBuilder.setSpan(span, 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableStringBuilder;
}
}
在网上查了下,实现字体渐变都是去重写了TextView,这会导致一个问题,就是这个TextView显示的的所有文字,都是这种效果,如果只要部分字体渐变,那就麻烦了。使用Span的好处就是,无需改动原来字体的颜色,实现部分字体变色。
实现的原理就是利用ReplacementSpan和LinearGradient两个类,ReplacementSpan的实现类ImageSpan应该是用的最多的了,自己看代码吧,至于LinearGradient可以实现好多的渐变效果,至于它的使用网上一大堆,可以实现上下渐变,左右渐变,对角线渐变,想怎么完怎么完,这个类大多时候都是用在绘制背景上了。
为什么会想到这个实现呢,完全是个mistake导致的,最近公司的设计师老是喜欢用渐变色来表达他们对这个世界的看法,背景你给我来个渐变,,这个阴影来个渐变,这两个字的背景你来个渐变,还要带点圆角。。。。。哎,谁叫人家是专业的,那就做吧。做个背景渐变带圆角的的时候,圆角span网上有了,就不贴了,也是重写了ReplacementSpan, 那怎么设置字体背景渐变呢,当然会想到LinearGradient,看代码
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
LinearGradient gradient = new LinearGradient(0, 0, 0, paint.descent() - paint.ascent(),
startColor,
endColor,
Shader.TileMode.MIRROR);
paint.setShader(gradient);
RectF oval = new RectF(x, y + paint.ascent(), x + mSize, y + paint.descent());
canvas.drawRoundRect(oval, mRadius, mRadius, paint);
**paint.setShader(null);//绘制完背景,设置为空**
paint.setColor(Color.WHITE);
paint.setTextSize(paint.getTextSize());
int size = (int) (paint.measureText(text, start, end));
canvas.drawText(text, start, end, x - 1 + (oval.width() - size) / 2, y, paint);//绘制文字
}
上面这段代码中 paint.setShader(null)当时并没有在绘制完背景设置为空,导致后面绘制字体一直显示不出来,绘制字体同样使用了这个渐变色绘制,查了半天,才发现这个问题。。。。。,想到这里既然渐变的字体背景都能绘制那么字体渐变不都是一样的原理了,然后就出现了LinearGradientFontSpan了。搞定,睡觉了zzzZZZZZZ。
上面的方法有一个严重的问题,当需要渐变的字数较长时,会导致无法换行!!!
canvas.drawText(text, start, end, x - 1 + (oval.width() - size) / 2, y, paint);//绘制文字
主要是因为这里没做换行处理。本来想用StaticLayout处理,但会导致OOM。没想到好的解决方法。但是做到这里,想到了ForegroundColorSpan,想来想去Span就那么几个,看下源码就能变异出另一种解决方法。新得实现方式如下,很简单
public class LinearGradientFontSpan2 extends CharacterStyle
implements UpdateAppearance {
private int startColor;
private int endColor;
public LinearGradientFontSpan2(int startColor, int endColor) {
this.startColor = startColor;
this.endColor = endColor;
}
@Override
public void updateDrawState(TextPaint tp) {
LinearGradient lg = new LinearGradient(0, 0, 0, tp.descent() - tp.ascent(),
startColor,
endColor,
Shader.TileMode.REPEAT);
tp.setShader(lg); //这里注意这里画出来的渐变色会受TextView的字体色的透明度影响
}
}