android自定义view实现字体变色

来源:互联网 发布:网络竞价排名 编辑:程序博客网 时间:2024/06/05 02:51

上次看了鸿洋的博客,其中有一篇博客实现的是字体逐渐变色的效果,不过其使用的是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方法

@Overrideprotected 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
//先绘制srccanvas.drawBitmap(getSrcBitmap(width,height), 0 , 0 , paint);//设置Paint的Xfermodepaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//每次绘制之前将dst的宽度加上10个像素,高度保持和src相同mRectWidth += 10;canvas.drawBitmap(getDesBitmap(mRectWidth,height), 0 , 0, paint);//重置当前Xfermodepaint.setXfermode(null);// 还原画布canvas.restoreToCount(sc);

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

// 创建一个矩形bitmap,作为src的bitmapprivate 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的bitmapprivate 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 0