Android-Paint高级-PorterDuffXfermode

其实在前面的文章中也都用到了画笔(Paint),了解了一些常用的属性,比如抗锯齿,带边框,空心,宽度等,这些都是最基本的属性,下面来说一个高级属性『PorterDuffXfermode』

PorterDuffXfermode

首先看一张图

PoerterDuffXfermode设置的是两个图层交集区域的显示方式,其中dst是先画的图形,src是后画的图形。

这里列举了16种,当然有的都不怎么经常使用,最常用的就是DST_IN、SRC_IN来将矩形图片变成圆角图片或者圆形图片了。下面就来进行一个实例CircleImageView(好久之前就看过鸿神的这个View,一直不怎么理解,书看到这一章节就忽然想起来了,自己重新写一遍),完成图如下:

Android-Paint高级-PorterDuffXfermode_第1张图片

分如下几步

  1. 获得图片
  2. 测量宽高
  3. 创建画布
  4. 画一个圆
  5. 使用PerterDuffXfermode来控制下一个图片和刚才的圆交集
  6. 画图片
  7. 完事

首先我们在构造函数中获得图片:

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

    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyleAttr, 0);
    try {
        mBitmap = BitmapFactory.decodeResource(getResources(), a.getResourceId(R.styleable.CircleImageView_src, -1));
        if (mBitmap == null) {
            throw new RuntimeException("src Null!");
        }
    } finally {
        a.recycle();
    }
}

然后我们在onMeasure()中控制图片的宽高

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    mWidth = measureWidthAndHeight(MeasureSpec.getSize(widthMeasureSpec));
    mHeight = measureWidthAndHeight(MeasureSpec.getSize(heightMeasureSpec));
    setMeasuredDimension(mWidth, mHeight);
}

private int measureWidthAndHeight(int size) {
    int mode = MeasureSpec.getMode(size);
    switch (mode) {
        case MeasureSpec.UNSPECIFIED:
        case MeasureSpec.AT_MOST:
            return 200;
        case MeasureSpec.EXACTLY:
        default:
            return size;
    }
}

这里我们给wrap_content设置了一个200的值,然后我们在onDraw()方法中完成我们剩下的工作:

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawBitmap(createBitmap(), 0, 0, null);
}

private Bitmap createBitmap() {
    /**
     * 获取宽高最小值,重新生成一个Bitmap
     */
    int min = Math.min(mHeight, mWidth);
    mBitmap = Bitmap.createScaledBitmap(mBitmap, min, min, false);

    /**
     * 根据原有的Bitmap再生成一个Bitmap,当做Canvas的参数
     * 如果在Canvas的构造中带入一个Bitmap的话,那么后续在画布上画的东西就等于在Bitmap上画的
     */
    Bitmap b = Bitmap.createBitmap(min, min, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(b);

    /**
     * 初始化画笔,设置抗锯齿
     */
    mPaint = new Paint();
    mPaint.setAntiAlias(true);

    /**
     * 首先画一个圆,和画布一样大
     */
    canvas.drawCircle(min / 2, min / 2, min / 2, mPaint);

    /**
     * 设置PoerterDuffXfermode参数,使后面画的和前面画的交集
     * 这样就等于在一个圆上画我们的图片,所以看到的就是一个圆形的图片了
     */
    mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
    canvas.drawBitmap(mBitmap, 0, 0, mPaint);
    return b;
}

这样就好啦,运行程序就可以看到我们开始的效果啦~

感谢鸿洋大神

原文地址


第二个实例 刮刮卡

相信大家都玩过刮刮卡,记得没错好像是支付宝也弄过这个效果(好像是在支付成功后)

刮刮卡有两个图层,上面一层和下面的图片,上面的主要用来被刮掉,在初始状态下,上面的图层会掩盖住下面的图层,当用手刮上面的图层时,下面的会慢慢显示出来,这就需要用到DST_IN了。效果图如下:

Android-Paint高级-PorterDuffXfermode_第2张图片

分如下几步

  1. 初始化Paint和图片等
  2. 在onDraw()方法中绘制两个图片,首先绘制背景图,然后绘制遮罩层
  3. 在onTouchEvent()方法中绘制路径
  4. 使用DST_IN模式绘制在上图层就ok

首先初始化,代码如下:

private void init() {
    mPaint = new Paint();
    /**
     * 设置画笔透明度为0,设置PoerterDuffXfermode为DST_IN模式
     */
    mPaint.setAlpha(0);
    mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeJoin(Paint.Join.ROUND); //设置画笔图形样式
    mPaint.setStrokeCap(Paint.Cap.ROUND);   //设置画笔转弯连接处的风格
    mPaint.setStrokeWidth(50);  //设置宽度

    mPath = new Path();
    /**
     * 获取背景图片
     */
    mBgBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.a1);

    /**
     * 创建遮罩层Bitmap和背景图片一样大小,画上灰色
     */
    mFgBitmap = Bitmap.createBitmap(mBgBitmap.getWidth(), mBgBitmap.getHeight(), Bitmap.Config.ARGB_8888);
    mCanvas = new Canvas(mFgBitmap);
    mCanvas.drawColor(Color.GRAY);
}

然后绘制两个图层:

protected void onDraw(Canvas canvas) {
    /**
     * 首先绘制背景图,然后绘制遮罩层,这样遮罩层才会在背景图上面
     */
    canvas.drawBitmap(mBgBitmap, 0, 0, null);
    canvas.drawBitmap(mFgBitmap, 0, 0, null);
}

最后在onTouchEvent()方法中绘制路径:

public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        /**
         * 重置一下路径,移动到该点
         */
        case MotionEvent.ACTION_DOWN:
            mPath.reset();
            mPath.moveTo(event.getX(), event.getY());
            break;
        /**
         * 路径的点
         */
        case MotionEvent.ACTION_MOVE:
            mPath.lineTo(event.getX(), event.getY());
            break;
    }

    /**
     * 绘制路径,其实是在mFgBitmap上绘制
     * 并且由于DST_IN模式,取交集,透明度为0,所以就能实现刮刮卡的效果
     */
    mCanvas.drawPath(mPath, mPaint);

    /**
     * 通知重绘
     */
    invalidate();
    return true;
}

这样一个刮刮卡的效果就完成了,是不是很简单。

需要注意的一点:在使用PorterDuffXfermode时,最好把硬件加速关闭,因为有的模式不支持硬件加速


最后

爱生活,爱小丽,爱Android

你可能感兴趣的:(Android-Paint高级-PorterDuffXfermode)