Android绘图机制与处理技巧(四)——Android图像处理之画笔特效处理

原创 2016年06月01日 08:51:47

除了常用的画笔属性,比如普通的画笔(Paint),带边框、填充的style,颜色(Color),宽度(StrokeWidth),抗锯齿(ANTI_ALIAS_FLAG)等,Android还提供了各种各样专业的画笔工具,如记号笔、毛笔、蜡笔等,使用它们可以实现更加丰富的效果。

PorterDuffXfermode

下图中列举了16种PorterDuffXfermode,有点像数学中集合的交集、并集这样的概念,它控制的是两个图像间的混合显示模式。

这里写图片描述

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

这些模式也不是经常使用,用的最多的是,使用一张图片作为另一张图片的遮罩层,通过控制遮罩层的图形,来控制下面被遮罩图形的显示效果。其中最常用的就是通过DST_IN、SRC_IN模式来实现将一个矩形图片变成圆角图片或者圆形图片的效果。

圆角矩形

要使用PorterDuffXfermode非常简单,只需要让画面拥有这个属性就可以了。比如要实现下面圆角矩形的效果:

这里写图片描述

先用一个普通画笔画一个Mask遮罩层,再用带PorterDuffXfermode的画笔将图像画在遮罩层上,这样就可以通过上面所说的效果来混合两个图像了,代码如下:

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test1);
        mBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(mBitmap);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        canvas.drawRoundRect(0, 0, bitmap.getWidth(), bitmap.getHeight(), 80, 80, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap, 0, 0, paint);

刮刮卡效果

若要实现刮刮卡效果(刮刮卡一般有两个图层,即上面的用来被刮掉的图层和下面隐藏的图层)。在初始状态下,上面的图层会将下面整个图层覆盖,当你用手刮上面的图层的时候,下面的图层会慢慢显示出来,类似于画图工具中的橡皮擦效果。

  • 首先要做一些初始化工作,例如准备好图片,设置好Paint的一些属性,代码如下:
        mPaint = new Paint();
        /**
         * 将画笔的透明度设置为0,这样才能显示出擦除效果。
         * 在使用PorterDuffXfermode进行图层混合时,并不是简单地只进行图层的计算,同时也会计算透明通道的值。
         * 正是由于混合了透明通道,才形成了这样的效果。
         */
        mPaint.setAlpha(0);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        mPaint.setStyle(Paint.Style.STROKE);
        //使Paint的笔触更加圆滑一点
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeWidth(50);
        //使Paint的连接处更加圆滑一点
        mPaint.setStrokeCap(Paint.Cap.ROUND);

        mPath = new Path();
        mBgBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
        mFgBitmap = Bitmap.createBitmap(mBgBitmap.getWidth(), mBgBitmap.getHeight(), Bitmap.Config.ARGB_8888);

        mCanvas = new Canvas(mFgBitmap);
        mCanvas.drawColor(Color.GRAY);
  • 获取用户手指滑动所产生的路径并将使用DST_IN模式将路径绘制到前面覆盖的图层上,代码如下所示。使用Path保存用户手指划过的痕迹。当然,如果使用贝塞尔曲线来做优化则会得到更好的显示效果,这里为了简化功能,就不使用贝塞尔曲线了。
    @Override
    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;
        }
        //使用DST_IN模式将路径绘制到前面覆盖的图层上
        mCanvas.drawPath(mPath, mPaint);
        invalidate();
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(mBgBitmap, 0, 0, null);
        canvas.drawBitmap(mFgBitmap, 0, 0, null);
    }

效果图如下所示:

这里写图片描述

在使用PorterDuffXfermode时还有一点需要注意,那就是最好在绘图时,将硬件加速关闭,因为有些模式并不支持硬件加速。

Shader

Shader又被称之为着色器、渲染器,它用来实现一系列的渐变、渲染效果。Android中的Shader包括以下几种。

  • BitmapShader——位图Shader
  • LinearGradient——线性Shader
  • RadialGradient——光束Shader
  • SweepGradient——梯度Shader
  • ComposeGradient——混合Shader

BitmapShader

与其他的Shader所产生的渐变不同,BitmapShader产生的是一个图像,这有点像Photoshop中的图像填充渐变。它的作用就是通过Paint对画布进行指定Bitmap的填充,填充时有以下几种模式可以选择。

  • CLAMP拉伸——拉伸的是图片最后的那一个像素,不断重复
  • REPEAT重复——横向、纵向不断重复
  • MIRROR镜像——横向不断翻转重复,纵向不断翻转重复

这里最常用的就是CLAMP拉伸模式,虽然它会拉伸最后一个像素,但是只要将图像设置为一定的大小,就可以避免这种拉伸。下面将一个矩形的图片变成一张圆形的图片,效果如下:

这里写图片描述

代码如下:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
        BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        Paint paint = new Paint();
        //用一张图片创建一支具有图像填充功能的画笔,并使用这只画笔绘制一个圆形
        paint.setShader(bitmapShader);
        canvas.drawCircle(500, 250, 200, paint);
    }

如果把TileMode改为REPEAT,并将Bitmap改为较小的ic_launcher图标,就可以看到几种模式的区别了,代码如下:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
        BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        Paint paint = new Paint();
        paint.setShader(bitmapShader);
        canvas.drawCircle(500, 250, 200, paint);
    }

运行程序,效果图如下:

这里写图片描述

LinearGradient

要使用LinearGradient非常简单,只需要指定渐变的起始颜色即可,代码如下:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint = new Paint();
        paint.setShader(new LinearGradient(0, 0, 400, 400, Color.BLUE, Color.YELLOW, Shader.TileMode.REPEAT));
        canvas.drawRect(0, 0, 400, 400, paint);
    }

效果图如下图,他是一个从(0, 0)到(400, 400)的由蓝色到黄色的渐变效果。

这里写图片描述

LinearGradient方法参数中的TileMode与在BitmapShader中的含义基本相同,这里将绘制矩形的大小设置为与渐变图像大小相同,所以没有看见REPEAT的效果。如果将图形扩大,REPEAT的效果就出来了,如下图所示:

这里写图片描述

倒影效果

这些渐变效果通常不会直接使用在程序里。通常情况下,把这种渐变效果作为一个遮罩层来使用,同时结合PorterDuffXfermode。这样处理后,遮罩层就不再是一个生硬的图形,而是一个具有渐变效果的图层。这样处理的效果会更加柔和、更加自然。下面用LinearGradient和PorterDuffXfermode来创建一个具有倒影效果的图片,效果图如下:

这里写图片描述

代码如下:

public class ReflectView extends View {

    private Bitmap mSrcBitmap, mRefBitmap;
    private Paint mPaint;
    private PorterDuffXfermode mXfermode;

    public ReflectView(Context context) {
        super(context);
        initRes(context);
    }

    public ReflectView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initRes(context);
    }

    public ReflectView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initRes(context);
    }

    private void initRes(Context context) {
        mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
        Matrix matrix = new Matrix();
        //实现图片的垂直翻转,避免使用旋转变换的复杂计算
        matrix.setScale(1, -1);
        mRefBitmap = Bitmap.createBitmap(mSrcBitmap, 0, 0, mSrcBitmap.getWidth(),
                mSrcBitmap.getHeight(), matrix, true);

        mPaint = new Paint();
        mPaint.setShader(new LinearGradient(0, mSrcBitmap.getHeight(), 0,
                mSrcBitmap.getHeight() + mSrcBitmap.getHeight() / 2, 0XDD000000, 0X10000000,
                Shader.TileMode.CLAMP));
        mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.BLACK);
        //绘制原图
        canvas.drawBitmap(mSrcBitmap, 0, 0, null);
        //绘制倒影图
        canvas.drawBitmap(mRefBitmap, 0, mSrcBitmap.getHeight(), null);
        mPaint.setXfermode(mXfermode);
        //绘制渐变效果矩形
        canvas.drawRect(0, mSrcBitmap.getHeight(), mSrcBitmap.getWidth(), mSrcBitmap.getHeight() * 2, mPaint);
        mPaint.setXfermode(null);
    }
}

PathEffect

PathEffect就是指用用各种笔触效果来绘制路径。Android系统提供了如下中展示的几种绘制PathEffect的方式,从上到下依次是:

这里写图片描述

  • 没效果
  • CornerPathEffect——就是将拐角处变得圆滑,具体圆滑的程度,则由参数决定
  • DiscretePathEffect——使用这个效果之后,线段上就会产生许多杂点
  • DashPathEffect——这个效果可以用来绘制虚线,用一个数组来设置各个点之间的间隔。此后绘制虚线时就重复这样的间隔进行绘制,另一个参数phase则用来控制绘制时数组的一个偏移量,通常可以通过设置值来实现路径的动态效果。
  • PathDashPathEffect——这个效果与DashPathEffect类似,只不过它的功能更加强大,可以设置显示点的图形,即方形点的虚线、圆形点的虚线。
  • ComposePathEffect——通过ComposePathEffect来组合PathEffect,就是将任意两种路径特性组合起来形成一个新的效果。

上图实现代码如下:

public class PathEffectView extends View {

    private Paint mPaint;
    private Path mPath;
    private PathEffect[] mEffects;

    public PathEffectView(Context context) {
        super(context);
        init();
    }

    public PathEffectView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public PathEffectView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
        mPaint.setColor(Color.DKGRAY);

        //使用随机数来生成一些随机的点形成一条路径
        mPath = new Path();
        mPath.moveTo(0, 0);
        for (int i = 0; i < 30; i++) {
            mPath.lineTo(i * 35, (float) (Math.random() * 100));
        }

        mEffects = new PathEffect[6];
        mEffects[0] = null;
        mEffects[1] = new CornerPathEffect(30);
        mEffects[2] = new DiscretePathEffect(3.0f, 5.0f);
        mEffects[3] = new DashPathEffect(new float[]{20, 10, 5, 10}, 0);
        Path path = new Path();
        path.addRect(0, 0, 8, 8, Path.Direction.CCW);
        mEffects[4] = new PathDashPathEffect(path, 12, 0, PathDashPathEffect.Style.ROTATE);
        mEffects[5] = new ComposePathEffect(mEffects[3], mEffects[1]);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < mEffects.length; i++) {
            mPaint.setPathEffect(mEffects[i]);
            canvas.drawPath(mPath, mPaint);
            canvas.translate(0, 200);
        }
    }
}

代码地址

版权声明:本文为博主原创文章,未经博主允许不得转载。

Android自定义View之画笔与画布

现在做的这个项目需要一个折线图的功能,当时想着使用第三方库来实现,结果总不能令我满意,只能通过自定义View用画笔去画了,这才发现 妈的 这东西我不会 赶紧去百度下,后来李大神把他画的折线图给我了,我...
  • u010069940
  • u010069940
  • 2016年07月29日 17:43
  • 2294

安卓画笔实现的探索

今日科技快讯 近日,科学家已经研发出一款人工智能系统,能够在不到1秒钟内自动探测结肠直肠癌。日本昭和大学研究人员研发出的这款计算机诊断系统使用了一种表皮分析图像,它能够将结肠直肠息肉影像...
  • c10WTiybQ1Ye3
  • c10WTiybQ1Ye3
  • 2017年11月01日 00:00
  • 209

android画笔错位问题的解决

下面的画画板的代码: public class MainActivity extends Activity { private ImageView iv; private Bitmap base...
  • oyty892968
  • oyty892968
  • 2013年09月06日 20:09
  • 1288

android Paint画笔的基本使用

1.setStyle(Paint.Style style)  设置画笔样式,取值有 Paint.Style.FILL :填充内部 Paint.Style.FILL_AND_STROKE :填充内部和描...
  • ttjay000
  • ttjay000
  • 2017年06月29日 10:48
  • 411

Android 的画布Canvas和画笔Paint

版权声明:本文为转载http://blog.csdn.net/rhljiayou/article/details/7212620 目录(?)[-] Class Overview...
  • sunanter
  • sunanter
  • 2016年03月18日 18:58
  • 1234

简单的Android画笔Demo

  • 2016年01月08日 10:53
  • 1.34MB
  • 下载

android画笔的基本属性

往Canvas上面draw需要一个Paint Paint p = new Paint(); //设置画笔的颜色 p.setColor(Color.par...
  • a85854783
  • a85854783
  • 2016年01月12日 15:24
  • 826

Android自定义控件画笔Paint介绍

画笔Paint我们总用到,现在介绍一下API,给自己保留复习一下                  Paint即画笔,在绘图过程中起到了极其重要的作用,画笔主要保存了颜色,        样...
  • yanglei0917
  • yanglei0917
  • 2016年08月18日 13:30
  • 1210

android中实现毛笔效果(View 中绘图)

最近有一个项目设计一个APP实现通过触摸屏实现毛笔写字效果。传统的绘画板程序直接通过Path的moveTo和LineTo便可实现简单的线条绘画程序。然而要达到毛笔的笔锋效果则需要更为详细点的设计。我的...
  • mybaby525
  • mybaby525
  • 2014年08月27日 22:37
  • 3958

Android 在Canvas中实现画笔效果(一)--钢笔

如题:
  • SBDUXING
  • SBDUXING
  • 2014年09月17日 15:34
  • 2079
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android绘图机制与处理技巧(四)——Android图像处理之画笔特效处理
举报原因:
原因补充:

(最多只允许输入30个字)