您当前的位置: 首页 >  android
  • 0浏览

    0关注

    674博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Android自定义view--图形叠加Xfermode与PorterDuff

沙漠一只雕得儿得儿 发布时间:2020-04-29 11:14:40 ,浏览量:0

 首先来看下各个场景的效果,项目完整代码:https://github.com/buder-cp/CustomView/tree/master/buder_DN_view/buderdn04

 上述效果的实现主要是使用图形的fermode与PorterDuff属性的叠加,下面是18中图形叠加模式

运行效果图:

1)PorterDuff.Mode.ADD:

 饱和度叠加

2)PorterDuff.Mode.CLEAR:

 所绘制不会提交到画布上,嗯结果...不知道是为什么了,正常是没东西的..

3)PorterDuff.Mode.DARKEN:

 取两图层全部区域,交集部分颜色加深

4)PorterDuff.Mode.DST:

 只保留目标图的alpha和color,所以绘制出来只有目标图

5)PorterDuff.Mode.DST_ATOP:

 源图和目标图相交处绘制目标图,不相交的地方绘制源图

6)PorterDuff.Mode.DST_IN:

 两者相交的地方绘制目标图,绘制的效果会受到原图处的透明度影响

7)PorterDuff.Mode.DST_OUT:

 在不相交的地方绘制目标图

8)PorterDuff.Mode.DST_OVER:

 目标图绘制在上方

9)PorterDuff.Mode.LIGHTEN:

 取两图层全部区域,点亮交集部分颜色

10)PorterDuff.Mode.MULTIPLY:

 取两图层交集部分叠加后颜色

11)PorterDuff.Mode.OVERLAY:

 叠加

12)PorterDuff.Mode.SCREEN:

 取两图层全部区域,交集部分变为透明色

13)PorterDuff.Mode.SRC:

 只保留源图像的alpha和color,所以绘制出来只有源图

14)PorterDuff.Mode.SRC_ATOP:

 源图和目标图相交处绘制源图,不相交的地方绘制目标图

15)PorterDuff.Mode.SRC_IN:

 两者相交的地方绘制源图

16)PorterDuff.Mode.SRC_OUT:

 不相交的地方绘制源图

17)PorterDuff.Mode.SRC_OVER:

 把源图绘制在上方

18)PorterDuff.Mode.XOR:

 不相交的地方按原样绘制源图和目标图

圆角图形:

通过PorterDuff.Mode.DST_IN模式来实现! 我们来分析分析实现流程原理:

Xfermode无非是两层图构成,先绘制的叫DST图(目标图),后绘制的叫SRC图(原图),我们要实现 圆形或者圆角,我们可以先把要显示的图片绘制出来(DST),这里我们通过src的属性进行了设置; 接着再绘制出圆形和圆角(SRC),我们想显示的部分是他们相交的地方,而且是图片部分的内容, 所以选择:DST_IN模式!

public class RoundImageView_SRCIN extends View {
    private Paint mBitPaint;
    private Bitmap BmpDST,BmpSRC;

    public RoundImageView_SRCIN(Context context, AttributeSet attrs) {
        super(context, attrs);
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        mBitPaint = new Paint();
        BmpDST = BitmapFactory.decodeResource(getResources(),R.drawable.shade,null);
        BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.xyjy6,null);
    }



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

        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);

        canvas.drawBitmap(BmpDST,0,0,mBitPaint);
        mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(BmpSRC,0,0,mBitPaint);

        mBitPaint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }
}
不规则wave波浪

实现和圆角图片是一样的,只是这里我们利用属性动画不断的移动波浪这张图片,仍然使用PorterDuff.Mode.DST_IN这个属性完成:

public class IrregularWaveView_DSTIN extends View {

    private Paint mPaint;
    private int mItemWaveLength = 0;
    private int dx = 0;

    private Bitmap BmpSRC, BmpDST;

    public IrregularWaveView_DSTIN(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();
        BmpDST = BitmapFactory.decodeResource(getResources(),R.drawable.wav,null);
        BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.circle_shape,null);
        mItemWaveLength = BmpDST.getWidth();

        startAnim();

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
        canvas.drawBitmap(BmpDST,new Rect(dx,0,dx+BmpSRC.getWidth(),BmpSRC.getHeight()),new Rect(0,0,BmpSRC.getWidth(),BmpSRC.getHeight()),mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.drawBitmap(BmpSRC,0,0,mPaint);
        mPaint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }

    private void startAnim() {
        ValueAnimator animator = ValueAnimator.ofInt(0,mItemWaveLength);
        animator.setDuration(4000);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                dx = (int) animation.getAnimatedValue();
                postInvalidate();
            }
        });
        animator.start();
    }
}
普通画板和贝塞尔画板

这两个画板是为了后面的橡皮擦效果的铺垫,在使用普通画板时连续的拐弯操作会出现不平滑现象,这是因为连线是直线在弯曲处无法做到顺滑,利用贝塞尔曲线即可,具体代码见项目。

public class BezierGestureTrackView extends View {

    private Path mPath = new Path();
    private Paint mPaint;
    private float mPreX, mPreY;


    public BezierGestureTrackView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                mPath.moveTo(event.getX(), event.getY());
                mPreX = event.getX();
                mPreY = event.getY();
                return true;
            }
            case MotionEvent.ACTION_MOVE: {
                float endX = (mPreX + event.getX()) / 2;
                float endY = (mPreY + event.getY()) / 2;
                mPath.quadTo(mPreX, mPreY, endX, endY);
                mPreX = event.getX();
                mPreY = event.getY();
                invalidate();
            }
            break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.WHITE);
        canvas.drawPath(mPath, mPaint);
    }
}
橡皮擦

我们来分析下实现流程:

  • 其实就是一个Bitmap,然后通过一个Path来记录用户绘制出来的图形,然后为我们的画笔设置DST_OUT的模式,那么 与Path重叠部分的DST(目标图),就是完整的图,在和手指划过的路径相交就会变成透明!
  • 接着设置下画笔,圆角,笔宽,抗锯齿等!
  • 再接着定义一个画Path,即用户绘制区域的方法,设置Xfermode后画区域而已!
  • 然后重写onTouchEvent方法,这部分和之前的自定义画图板是一样的!
  • 最后重写onDraw()方法,先绘制背景图片,调用用户绘制区域的方法,再绘制前景图片!
public class EraserView_SRCOUT extends View {

    private Paint mBitPaint;
    private Bitmap BmpDST, BmpSRC;
    private Path mPath;
    private float mPreX, mPreY;

    public EraserView_SRCOUT(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        mBitPaint = new Paint();
        mBitPaint.setColor(Color.RED);
        mBitPaint.setStyle(Paint.Style.STROKE);
        mBitPaint.setStrokeWidth(45);

        BmpSRC = BitmapFactory.decodeResource(getResources(), R.drawable.xyjy6, null);
        BmpDST = Bitmap.createBitmap(BmpSRC.getWidth(), BmpSRC.getHeight(), Bitmap.Config.ARGB_8888);
        mPath = new Path();
    }

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

        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);

        //先把手指轨迹画到目标Bitmap上
        Canvas c = new Canvas(BmpDST);
        c.drawPath(mPath, mBitPaint);

        //然后把目标图像画到画布上
        canvas.drawBitmap(BmpDST, 0, 0, mBitPaint);

        //计算源图像区域
        mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
        canvas.drawBitmap(BmpSRC, 0, 0, mBitPaint);

        mBitPaint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mPath.moveTo(event.getX(), event.getY());
                mPreX = event.getX();
                mPreY = event.getY();
                return true;
            case MotionEvent.ACTION_MOVE:
                float endX = (mPreX + event.getX()) / 2;
                float endY = (mPreY + event.getY()) / 2;
                mPath.quadTo(mPreX, mPreY, endX, endY);
                mPreX = event.getX();
                mPreY = event.getY();
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        postInvalidate();
        return super.onTouchEvent(event);
    }

}

 

关注
打赏
1657159701
查看更多评论
立即登录/注册

微信扫码登录

0.0667s