android自定义View----文字部分渐变效果

今天做的是一个简单支持文字部分渐变效果的控件,还是先放上成果:


android自定义View----文字部分渐变效果_第1张图片
loading

android自定义View----文字部分渐变效果_第2张图片
tab

如上图,这个控件可以做特殊的loading动画,比如下载、上传、等,也可以用在viewpager切换时的tab,实现文字部分变色等。

  • 实现原理:

  • 拿到文字,先把它渲染在画布上,作为底色

  • 然后对画布进行矩形裁剪clipRect(),paint换一种颜色,再把文字绘制一遍,即可

  • 裁剪的尺寸是根据外部传入的progress、渐变方向、以及文字的宽高确定

    • 先铺代码:

attrs.xml 比较简单,定义两种对比的颜色、文字大小和文字内容 :


        
        
        
        
    
  • 写一个view的子类,(其实继承TextView更方便一点)

全局变量

    private int mDirection ; //渐变的方向

    public static final int DIRECTION_LEFT = 0;
    public static final int DIRECTION_RIGHT = 1;
    public static final int DIRECTION_TOP = 2;
    public static final int DIRECTION_BOTTOM = 3;

    public void setDirection(int direction) {
        mDirection = direction;
        postInvalidate();
    }

    private Paint mPaint;

    private String mText;//显示的文字

    private int mTextSize;//文字大小

    private float mProgress;//渐变位置

    private int mFirstColor;//base文字颜色

    private int mSecondColor;//变化的文字颜色

    public void setmProgress(float mProgress) {
        this.mProgress = mProgress;
        postInvalidate();
    }

构造器,我的千篇一律的写法~~

    public ShadeTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ShadeTextView, 0, defStyleAttr);

        for (int i = 0; i < typedArray.length(); i++) {
            int attr = typedArray.getIndex(i);
            switch (attr) {
                case R.styleable.ShadeTextView_text:
                    mText = typedArray.getString(attr);
                    break;
                case R.styleable.ShadeTextView_firstColor:
                    mFirstColor = typedArray.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.ShadeTextView_secondColor:
                    mSecondColor = typedArray.getColor(attr, Color.BLUE);
                    break;
                case R.styleable.ShadeTextView_textSize:
                    mTextSize = typedArray.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
                            TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
                    break;
                /*case R.styleable.ShadeTextView_progress:
                    mProgress = typedArray.getInteger(attr, 30);
                    break;*/
            }
        }
        typedArray.recycle();
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setTextSize(mTextSize);
    }

onMeasure()的处理,如果继承TextView的话可以省略很多代码。。。

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int specMode = MeasureSpec.getMode(widthMeasureSpec);
        int specSize = MeasureSpec.getSize(widthMeasureSpec);

        int width = 0, height = 0;

        switch (specMode) {
            case MeasureSpec.EXACTLY:
                width = specSize + getPaddingRight() + getPaddingLeft();
                break;

            case MeasureSpec.AT_MOST:
                width = (int) (mPaint.measureText(mText) + getPaddingLeft() + getPaddingRight());//先确定mPaint是否已经设置textsize
                break;
        }

        specMode = MeasureSpec.getMode(heightMeasureSpec);
        specSize = MeasureSpec.getSize(heightMeasureSpec);
        switch (specMode) {
            case MeasureSpec.EXACTLY:
                height = specSize + getPaddingTop() + getPaddingBottom();
                break;
            case MeasureSpec.AT_MOST:
                height = (int) (Math.abs(mPaint.getFontMetrics().bottom - mPaint.getFontMetrics().top) + getPaddingTop() + getPaddingBottom());
                break;
        }

        setMeasuredDimension(width, height);

    }

重头戏 onDraw(),本篇的思想精华所在。

    @Override
    protected void onDraw(Canvas canvas) {
        float textWidth = mPaint.measureText(mText);
        float mLeft = (getMeasuredWidth() - textWidth) / 2;
        float textHeight = Math.abs(mPaint.getFontMetrics().bottom - mPaint.getFontMetrics().top);
        float mTop = (getMeasuredHeight() - textHeight) /2 ;

        //先画底层的文字
        mPaint.setColor(mFirstColor);
        canvas.drawText(mText, mLeft, getY(), mPaint);
      
        mPaint.setColor(mSecondColor);
        // 接着根据传入的渐变方向、颜色等来裁剪,接着在同样的位置重新渲染文字
        if (mDirection == DIRECTION_LEFT) {
            canvas.save(Canvas.CLIP_SAVE_FLAG);
            canvas.clipRect(mLeft, 0, textWidth * mProgress + mLeft, getMeasuredHeight());
            canvas.drawText(mText, mLeft, getY(), mPaint);
            canvas.restore();
        } else if (mDirection == DIRECTION_RIGHT){
            canvas.save(Canvas.CLIP_SAVE_FLAG);
            canvas.clipRect(textWidth - textWidth * mProgress + mLeft , 0 , textWidth + mLeft, getMeasuredHeight());
            canvas.drawText(mText, + mLeft, getY(), mPaint);
            canvas.restore();
        } else if (mDirection == DIRECTION_TOP){
            canvas.save(Canvas.CLIP_SAVE_FLAG);
            canvas.clipRect(0, mTop , getWidth(), textHeight * mProgress + mTop);
            canvas.drawText(mText, + mLeft, getY(), mPaint);
            canvas.restore();
        }else if (mDirection == DIRECTION_BOTTOM){
            canvas.save(Canvas.CLIP_SAVE_FLAG);
            canvas.clipRect(0, textHeight - textHeight * mProgress + mTop, getWidth(), textHeight + mTop );
            canvas.drawText(mText, + mLeft, getY(), mPaint);
            canvas.restore();
        }
    }
    public float getY() {
        Paint.FontMetricsInt fm = mPaint.getFontMetricsInt();
        return (getHeight() + fm.descent - fm.ascent) / 2 - fm.descent;
    }

值得说明的是,
float textHeight = Math.abs(mPaint.getFontMetrics().bottom - mPaint.getFontMetrics().top);
return (getHeight() + fm.descent - fm.ascent) / 2 - fm.descent;
这里借鉴了Android 自定义View-怎么绘制居中文本?的研究成果,怒学。

  • View基本写完,可以先测试了,先用seekbar测试四个方向的渐变效果,代码比较简单,只贴一段根据seekbar的progress值设置我们自定义View的渐变方向和渐变位置的代码:

 @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                shadeTextView.setDirection(ShadeTextView.DIRECTION_TOP);
                shadeTextView.setmProgress(progress * 1.0f / 100);
            }

运行测试,基本OK


android自定义View----文字部分渐变效果_第3张图片

运用到viewpager中的代码也比较简单,只贴核心调用部分,其他大家都很熟悉了。

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
       if (positionOffset > 0){

            ShadeTextView left = shadeTextViews.get(position);
            ShadeTextView right = shadeTextViews.get(position + 1);

            left.setDirection(1);
            right.setDirection(0);

            left.setmProgress(1-positionOffset);
            right.setmProgress(positionOffset);
      }
}

然后效果就是文首的viewpager切换效果了。

源码点击查看

你可能感兴趣的:(android自定义View----文字部分渐变效果)