Android 自定义View实现“歌词“染色动画

原创
2016/01/15 15:57
阅读数 4.2W

先来看效果

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



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);
    }
  }

}

 

第二种效果



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值是基线位置

  3. mTextHeight = fontMetrics.bottom-fontMetrics.descent-fontMetrics.ascent;//获得文本的高度
  4. 注意内容去的尺寸大小以及图片合成模式

  5. 注意LinearGradient的最后一个参数

  6. 自己去试试吧,我这些代码不够完善。

展开阅读全文
加载中
点击加入讨论🔥(2) 发布并加入讨论🔥
打赏
2 评论
44 收藏
2
分享
返回顶部
顶部