android自定义view实现字体变色

上次看了鸿洋的博客,其中有一篇博客实现的是字体逐渐变色的效果,不过其使用的是clipRect实现的,今天给大家带来另一种实现方式,通过XferMode来实现。先看效果吧。
这里写图片描述

先说下实现原理:我们首先绘制当前显示的文本内容,然后设置xfermode的值为PorterDuff.Mode.SRC_IN,然后子啊绘制一个和我们当前view高度相同,并且宽度不断增大的绿色矩形。废话不多说,下面看具体实现:

自定义属性

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <attr name="text_value" format="string"/>
    <attr name="text_size" format="dimension"/>
    <declare-styleable name="filter_text">
        <attr name="text_value" />
        <attr name="text_size" />
    </declare-styleable>

</resources>

这里我只定义了两个属性:

  • text_value 表示需要绘制的文本内容
  • text_size 表示绘制的文本字体的大小

获取自定义属性并且初始化

在构造方法中,获取自定义的属性,并且进行必要的初始化工作。

public FilterText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //获取自定义属性的值
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.filter_text);
        mTextValue = array.getString(R.styleable.filter_text_text_value);
        mTextSize = array.getDimensionPixelSize(R.styleable.filter_text_text_size, (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
        //TypedArray用完记得回收
        array.recycle();

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //设置文本的字体大小
        mPaint.setTextSize(mTextSize);
        mBound = new Rect();
        //用来计算当前mTextValue的宽度和高度
        mPaint.getTextBounds(mTextValue, 0, mTextValue.length(),mBound);

    }

这里需要注意,由于我们等下对于该自定义view的宽高会设置为wrap_content,所以需要重写onMeasure方法来测量我们当前的view的宽高,所以需要在测量之前设置好字体的大小,否则会影响测量值。

重写onMeasure方法

    /**
     * 测量当前view的大小
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        int finalWidth = 0;
        int finalHeight = 0;

        if (widthMode == MeasureSpec.EXACTLY) {
            //如果测量模式是EXACTLY类型,则直接使用推荐值加上padding的值
            finalWidth = widthSize + getPaddingLeft() + getPaddingRight();
        } else {
            finalWidth = getPaddingLeft() + getPaddingRight() + mBound.width();
            if (widthMode == MeasureSpec.AT_MOST) {
                //如果测量模式是AT_MOST,则取期望值和我们计算出的最小值
                finalWidth = Math.min(finalWidth, widthSize);
            }
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            finalHeight = heightSize + getPaddingBottom() + getPaddingTop();
        } else {
            finalHeight = getPaddingTop() + getPaddingBottom() + mBound.height();
            if (heightMode == MeasureSpec.AT_MOST) {
                finalHeight = Math.min(finalHeight, heightSize);
            }
        }
        //记得调用setMeasuredDimension方法
        setMeasuredDimension(finalWidth, finalHeight);
    }

这里首先获得宽高的测量模式和推荐的值,然后根据具体的测量模式来计算当前view的宽度和高度。最后记得调用setMeasuredDimension方法。

重写onDraw方法

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

        int width = getWidth();
        int height = getHeight();

        Paint paint = new Paint();

        //创建一个图层,在图层上演示图形混合后的效果
        int sc = canvas.saveLayer(0, 0, width, height, null, Canvas.MATRIX_SAVE_FLAG |
                Canvas.CLIP_SAVE_FLAG |
                Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
                Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
                Canvas.CLIP_TO_LAYER_SAVE_FLAG);

        //先绘制“dest
        canvas.drawBitmap(getDesBitmap(width,height), 0 , 0 , paint);
        //设置Paint的Xfermode
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        mRectWidth += 10;
        canvas.drawBitmap(getSrcBitmap(mRectWidth,height), 0 , 0, paint);
        //重置当前Xfermode
        paint.setXfermode(null);
        // 还原画布
        canvas.restoreToCount(sc);

        if (mRectWidth < width) {//不断重绘当前view
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            invalidate();
        }

    }

主要分为以下几步:

  • 首先获得当前view的宽度和高度
int width = getWidth();
int height = getHeight();
  • 创建一个图层,在图层上演示图形混合后的效果,注意一定要创建该图层,否则会出现两个图形交集之外部分为黑色
int sc = canvas.saveLayer(0, 0, width, height, null, Canvas.MATRIX_SAVE_FLAG |
                Canvas.CLIP_SAVE_FLAG |
                Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
                Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
                Canvas.CLIP_TO_LAYER_SAVE_FLAG);
  • 绘制src和dst
//先绘制src
canvas.drawBitmap(getSrcBitmap(width,height), 0 , 0 , paint);
//设置Paint的Xfermode
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//每次绘制之前将dst的宽度加上10个像素,高度保持和src相同
mRectWidth += 10;
canvas.drawBitmap(getDesBitmap(mRectWidth,height), 0 , 0, paint);
//重置当前Xfermode
paint.setXfermode(null);
// 还原画布
canvas.restoreToCount(sc);

这里的getSrcBitmap和getDesBitmap就是我们分别绘制的文本和过滤的背景色矩形,如下:

// 创建一个矩形bitmap,作为src的bitmap
private Bitmap getSrcBitmap(int w, int h) {
        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bm);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
        //绘制边框
        p.setStrokeWidth(3);
        p.setColor(Color.RED);
        p.setStyle(Paint.Style.STROKE);
        c.drawRect(0, 0, w, h, p);

        //绘制文字
        p.setColor(Color.BLACK);
        p.setStyle(Paint.Style.FILL);
        p.setTextSize(mTextSize);
        c.drawText(mTextValue,w / 2 - mBound.width() / 2, h / 2 + mBound.height() / 2, p);
        return bm;
}


// 创建一个矩形bitmap,作为dst的bitmap
private Bitmap getDesBitmap(int w, int h) {
        //首先创建一个位图
        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        //创建一个和当前位图相同大小的canvas对象
        Canvas c = new Canvas(bm);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
        p.setColor(Color.GREEN);
        c.drawRect(0,0,w,h ,p);
        return bm;
}

总结以下:

  • 测量当前view的宽和高,如果有字体大小需要在onMeasure测量之前就设置其大小,否则影响测量值
  • 在onDraw方法中具体的绘制工作:

  • 创建一个图层,在图层上演示图形混合后的效果
  • 先绘制src
  • 设置Paint的Xfermode,PorterDuff.Mode.SRC_IN
  • 绘制dst
  • 重置当前Xfermode
  • 还原画布

    ok,今天就到这里了,希望大家喜欢。

源码下载

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值