前段时间的需求,涉及到大量带各种效果的字体的使用,比如描边、渐变、阴影等。一般情况下,我们在Android开发中用到花里胡哨字体的情况不多。但是,拿到了这样的需求,我们还是要实现这样一款支持多种效果的字体。其实,网上也有一些实现各种效果字体的方法。这篇博客,将把描边,渐变,阴影等结合到一起,实现一款自定义的文本框。
一、撸代码前的思考
在开始动手之前,我们先明确一下需求。需求就是:一款支持描边、渐变、阴影效果的字体。为了实现上述的需求,我们需要做下面的工作:
1、文本框具有描边的属性
(1)是否描边:isStroke
(2)描边的颜色:strokeColor
(3)描边的宽度:strokeWidth
2、文本框具有颜色渐变的属性
(1)是否渐变:isGradient
(2)渐变是否是竖向的:isVertical
(3)渐变的起始颜色:startColor
(4)渐变的终止颜色:endColor
3、文本框具有阴影的属性
(1)是否显示阴影:isShadow
(2)阴影的颜色:shadowColor
(3)x方向的阴影:shadowX
(4)y方向的阴影:shadowY
(5)阴影的半径:shadowRadius
二、开始撸代码
1、在values文件夹下创建attrs.xml文件,把我们需求里的属性都定义好:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="GradientTextView">
<attr name="startColor" format="color" />
<attr name="endColor" format="color" />
<attr name="strokeColor" format="color"/>
<attr name="strokeWidth" format="dimension"/>
<attr name="isStroke" format="boolean" />
<attr name="isGradient" format="boolean" />
<attr name="isVertical" format="boolean" />
<attr name="isShadow" format="boolean"/>
<attr name="shadowX" format="integer"/>
<attr name="shadowY" format="integer"/>
<attr name="shadowRadius" format="integer"/>
<attr name="shadowColor" format="color"/>
</declare-styleable>
</resources>
2、初始化属性:
private void initAttrs(Context context, AttributeSet attrs) {
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.GradientTextView);
startColor = ta.getColor(R.styleable.GradientTextView_startColor, 0xfffff8);
endColor = ta.getColor(R.styleable.GradientTextView_endColor, 0xffd007);
strokeColor = ta.getColor(R.styleable.GradientTextView_strokeColor, 0x2a1505);
strokeWidth = ta.getDimension(R.styleable.GradientTextView_strokeWidth, 0);
isStroke = ta.getBoolean(R.styleable.GradientTextView_isStroke, false);
isGradient = ta.getBoolean(R.styleable.GradientTextView_isGradient, false);
isVertical = ta.getBoolean(R.styleable.GradientTextView_isVertical, true);
useTypeface = ta.getBoolean(R.styleable.GradientTextView_useTypeface, false);
isShadow = ta.getBoolean(R.styleable.GradientTextView_isShadow, false);
shadowX = ta.getInt(R.styleable.GradientTextView_shadowX, 1);
shadowY = ta.getInt(R.styleable.GradientTextView_shadowY, 1);
shadowRadius = ta.getInt(R.styleable.GradientTextView_shadowRadius, 1);
shadowColor = ta.getColor(R.styleable.GradientTextView_shadowColor, 0x000000);
}
3、根据属性判断是否需要应用效果:
(1)描边
这里简单说下描边的实现:其实就是绘制两个不一样大的TextView,大一点的就是描边的颜色,小一点的就是我们需要的字体的颜色,因此,我们需要创建一个新的TextView来实现描边:
if (isStroke) {
backGroundText = new TextView(context, attrs);
TextPaint tp1 = backGroundText.getPaint();
tp1.setStrokeWidth(strokeWidth);
tp1.setStyle(Paint.Style.STROKE);
backGroundText.setTextColor(strokeColor);
backGroundText.setGravity(getGravity());
}
(2)渐变
渐变的实现,其实就是设置一个Shader,也就是LinearGradient。说起Linear,我们就想起LinearLayout的Vertical和Horizontal属性,LinearGradient也需要一个标志着横向或者竖向的属性。因此,我们需要根据横向渐变还是竖向渐变去设置对应的shader:
if (isGradient) {
if (isVertical) {
mLinearGradient = new LinearGradient(0, 0, 0,
this.getPaint().getTextSize(),
startColor,
endColor,
Shader.TileMode.CLAMP);
} else {
mLinearGradient = new LinearGradient(0, 0, this.getWidth(),
0,
startColor,
endColor,
Shader.TileMode.CLAMP);
}
}
(3)阴影
阴影的实现,不得不说,确实费了些时间。我们都知道TextView本身就支持阴影效果。但是,我们自定义的字体,如果直接使用自带的阴影效果,会非常模糊,根本不是阴影的效果。因此,阴影的效果也需要我们自己去实现。实现的要点是:为TextView设置Layer,在设置完后,必须清空Layer再去绘制其他的地方。在这里,我就直接把所有的绘制贴出来了:
@Override
protected void onDraw(Canvas canvas) {
if (isShadow) {
getPaint().setShadowLayer(shadowRadius, shadowX, shadowY, shadowColor);
getPaint().setShader(null);
super.onDraw(canvas);
getPaint().clearShadowLayer();
getPaint().setShader(mLinearGradient);
super.onDraw(canvas);
} else {
if (isStroke) {
if (backGroundText != null) {
CharSequence tt = backGroundText.getText();
if (tt == null || !tt.equals(this.getText())) {
backGroundText.setText(getText());
this.postInvalidate();
}
}
backGroundText.draw(canvas);
}
if (isGradient) {
this.getPaint().setShader(mLinearGradient);
}
}
super.onDraw(canvas);
}
大家看到上面的代码,可能有些疑惑,在这里简单的说明一下:
(1)如果直接应用TextView自带的阴影属性,会很模糊,其实是把渐变的shader作为了阴影。因此,我们设置layer,同时设置shader为空。
(2)设置完layer后,先绘制阴影。绘制完阴影后,清空layer,设置渐变的属性,继续绘制渐变的效果。
(3)如果我们的文本内容是会变化的,那么我们需要及时同步描边的字体和文本内容的一致。因此,判断有描边的属性时,我们需要做一下文本的同步。
三、在布局文件中使用
<com.example.mygradient.GradientTextView
android:id="@+id/tv2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="12:45"
android:textSize="18sp"
app:endColor="#ffd007"
app:isGradient="true"
app:isStroke="false"
app:isVertical="true"
app:isShadow="true"
app:shadowColor="#df000000"
app:shadowRadius="1"
app:shadowX="1"
app:shadowY="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv1"
app:startColor="#fffff8"
app:useTypeface="false" />
以上就是多效果文本框的实现,核心的代码都已经给出,需要说明的地方也已经在上面详细说明了。