先来看效果


Android 自定义TextView实现文字渐变动画_Math


Android 自定义TextView实现文字渐变动画_Math_02

第一种效果的代码如下,主要是结合Shader实现的

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;

public class GradientShaderTextView extends TextView {

private LinearGradient mLinearGradient;
private Matrix mGradientMatrix;
private Paint mPaint;
private int mViewWidth = 0;
private int mTranslate = 0;

private boolean mAnimating = true;
private int delta = 15;
public GradientShaderTextView(Context ctx)
{
this(ctx,null);
}

public GradientShaderTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (mViewWidth == 0) {
mViewWidth = getMeasuredWidth();
if (mViewWidth > 0) {
mPaint = getPaint();
String text = getText().toString();
// float textWidth = mPaint.measureText(text);
int size;
if(text.length()>0)
{
size = mViewWidth*2/text.length();
}else{
size = mViewWidth;
}
mLinearGradient = new LinearGradient(-size, 0, 0, 0,
new int[] { 0x33ffffff, 0xffffffff, 0x33ffffff },
new float[] { 0, 0.5f, 1 }, Shader.TileMode.CLAMP); //边缘融合
mPaint.setShader(mLinearGradient);
mGradientMatrix = new Matrix();
}
}
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

int length = Math.max(length(), 1);
if (mAnimating && mGradientMatrix != null) {
float mTextWidth = getPaint().measureText(getText().toString());
mTranslate += delta;
if (mTranslate > mTextWidth+1 || mTranslate<1) {
delta = -delta;
}
mGradientMatrix.setTranslate(mTranslate, 0);
mLinearGradient.setLocalMatrix(mGradientMatrix);
postInvalidateDelayed(30);
}
}

}

第二种效果

import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.View;
import android.widget.TextView;

public class KTVTextView extends View {

private Paint mPaint;

private int delta = 15;

private float mTextHeight;
private float mTextWidth;

private PorterDuffXfermode xformode;
private String mText = "你是我生命里的一首歌";

public KTVTextView(Context ctx)
{
this(ctx,null);
}


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

public KTVTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);

mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
xformode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);

initViewAndDatas();

setOnClickListener(new View.OnClickListener(){

@Override
public void onClick(View v) {
postIndex = 0;
postInvalidate();
}
});

}

public void initViewAndDatas()
{
mPaint.setColor(Color.CYAN);
mPaint.setTextSize(40.0f);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setXfermode(null);
mPaint.setTextAlign(Paint.Align.LEFT);

Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
mTextHeight = fontMetrics.bottom-fontMetrics.descent-fontMetrics.ascent;
mTextWidth = mPaint.measureText(mText);
//文字精确高度
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int mWidth;
int mHeight;
/**
* 设置宽度
*/
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate
{
mWidth = specSize;
}
else
{
// 由图片决定的宽
int desireByImg = getPaddingLeft() + getPaddingRight()
+ 380;
if (specMode == MeasureSpec.AT_MOST)// wrap_content
{
mWidth = Math.min(desireByImg, specSize);
} else
mWidth = desireByImg;
}
/***
* 设置高度
*/
specMode = MeasureSpec.getMode(heightMeasureSpec);
specSize = MeasureSpec.getSize(heightMeasureSpec);
if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate
{
mHeight = specSize;
} else
{
int desire = getPaddingTop() + getPaddingBottom()
+ 80;

if (specMode == MeasureSpec.AT_MOST)// wrap_content
{
mHeight = Math.min(desire, specSize);
} else
{
mHeight = desire;
}
}

setMeasuredDimension((int) mWidth, (int) mHeight);
}

private int postIndex;

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int contentWidth = getWidth() - getPaddingLeft() - getPaddingRight();
int contentHeight = getHeight() - getPaddingTop() - getPaddingBottom();

Bitmap srcBitmap = Bitmap.createBitmap(getWidth(),getHeight(), Bitmap.Config.ARGB_8888);
Canvas srcCanvas = new Canvas(srcBitmap);
srcCanvas.drawText(mText, 0, getPaddingTop(), mPaint);

mPaint.setXfermode(xformode);
mPaint.setColor(Color.RED);
RectF rectF = new RectF(0,0,postIndex,mTextHeight);
srcCanvas.drawRect(rectF, mPaint);
canvas.drawBitmap(srcBitmap,getPaddingLeft(),getPaddingTop(), null);
initViewAndDatas();
if(postIndex<mTextWidth)
{
postIndex+=10;
postInvalidateDelayed(30);
}
}

}

注意:

  1. 文本绘制时必须和当前View保持同样的长宽尺寸,否则会出现文字变形问题
  2. 文本绘制的drawText(string,int x,int y,Paint paint);中的y值是基线位置
mTextHeight = fontMetrics.bottom-fontMetrics.descent-fontMetrics.ascent;//获得文本的高度
  1. 注意内容去的尺寸大小以及图片合成模式
  2. 注意LinearGradient的最后一个参数
  3. 自己去试试吧,我这些代码不够完善。