美文网首页安卓Android开发程序员
Android-Paint高级-PorterDuffXfermo

Android-Paint高级-PorterDuffXfermo

作者: c37d344afd22 | 来源:发表于2016-08-22 00:15 被阅读300次

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

    PorterDuffXfermode

    首先看一张图

    分如下几步

    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了。效果图如下:

    分如下几步

    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高级-PorterDuffXfermo

      本文链接:https://www.haomeiwen.com/subject/dxmwsttx.html