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

除了常用的画笔属性,比如普通的画笔(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);
        }
    }
}

代码地址

时间: 2024-12-13 15:20:58

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

第三章 Android绘图机制与处理技巧

1.屏幕尺寸信息 屏幕大小:屏幕对角线长度,单位“寸”:分辨率:手机屏幕像素点个数,例如720x1280分辨率:PPI(Pixels Per Inch):即DPI(Dots Per Inch),它是对角线的像素点数除以屏幕大小得到的:系统屏幕密度:android系统定义了几个标准的DPI值作为手机的固定DPI.(注,最后一个有笔误,正确的是1080x1920)独立像素密度(DP):android系统使用mdpi屏幕作为标准,在这个屏幕上1dp=1px,其他屏幕可以通过比例进行换算.在hdpi中,

Android群英传知识点回顾——第六章:Android绘图机制与处理技巧

6.1 屏幕的尺寸信息 6.1.1 屏幕参数 6.1.2 系统屏幕密度 6.1.3 独立像素密度dp 6.1.4 单位转换 6.2 2D绘图基础 6.3 Android XML绘图 6.3.1 Bitmap 6.3.2 Shape 6.3.3 Layer 6.3.4 Selector 6.4 Android绘图技巧 6.4.1 Canvas 6.4.2 Layer图层 6.5 Android图像处理之色彩特效处理 6.5.1 色彩矩阵分析 6.5.2 Android颜色矩阵--ColorMatr

Android绘图机制与处理技巧

一屏幕的尺寸信息 1屏幕参数 2系统屏幕密度 3独立像素密度dp 4单位换算 二2D绘图基础 三Android XML 绘图 Bitmap Shape Layer Selector 四绘图技巧 Canvas 一.屏幕的尺寸信息 1屏幕参数 屏幕大小 指屏幕对角线的长度,通常使用"寸"来度量,例如4.7寸手机 5.5寸手机等. 分辨率 分辨率是指手机屏幕的像素点个数,例如720*1280是指屏幕分变率,指宽有720个像素点,高有1280个像素点. PPI 每英寸像素(Pixels Per

Android绘图机制与处理技巧(二)——Android图像处理之色彩特效处理

Android对于图片处理,最常使用到的数据结构是位图--Bitmap,它包含了一张图片所有的数据.整个图片都是由点阵和颜色值组成的,所谓点阵就是一个包含像素的矩阵,每一个元素对应着图片的一个像素.而颜色值--ARGB,分别对应透明图.红.绿.蓝这四个通道分量,它们共同决定了每个像素点显示的颜色. 色彩矩阵分析 在色彩处理中,通常使用以下三个角度来描述一个图像. 色调--物体传播的颜色 饱和度--颜色的纯度,从0(灰)到100%(饱和)来进行描述 亮度--颜色的相对明暗程度 而在Android中

Android绘图机制(一) View类

对android绘图机制的理解,在Android学习中可谓至关重要,包括自定义控件也是使用非常频繁的内容.最近在项目中遇到一个比较棘手的问题,项目中好几个模块都用到ListView或者GridView的”下拉刷新,上拉加载更多“功能 .一开始在网上找了大牛写的作品,用在项目中后发现时不时会出现卡壳的现象,改进以后会有所改善,不过还是感觉有所欠缺.无奈我是个处女座菜鸟,尝试着找出这些问题的根本原因却发现无从下手,所以先补补基础.( 纯文字看着确实很费劲,所以顺便引用下其他的文章)  概述 View

Android群英传笔记——第七章:Android动画机制和使用技巧

Android群英传笔记--第七章:Android动画机制和使用技巧 想来,最近忙的不可开交,都把看书给冷落了,还有好几本没有看完呢,速度得加快了 今天看了第七章,Android动画效果一直是人家中十分重要的一部分,从早期的Android版本中,由于动画机制和绘图机制的不健全,Android的人机交互备受诟病,Android从4.X开始,特别是5.X,动画越来越完善了,Google也开始重视这一方面了,我们本章学习的主要内容有 Android视图动画' Android属性动画 Android动画

Android群英传知识点回顾——第七章:Android动画机制与使用技巧

7.1 Android View动画框架 7.1.1 透明度动画 7.1.2 旋转动画 7.1.3 位移动画 7.1.4 缩放动画 7.1.5 动画集合 7.2 Android属性动画分析 7.2.1 ObjectAnimator 7.2.2 PropertyValuesHolder 7.2.3 ValueAnimator 7.2.4 动画事件的监听 7.2.5 AnimatorSet 7.2.6 在XML中使用属性动画 7.2.7 View的animate方法 7.3 Android布局动画

Android动画机制与使用技巧(五)——Android 5.X SVG 矢量动画机制

Google在Android 5.X 中增加了对SVG 矢量图形的支持,这对于创建新的高效率动画具有非常重大的意义.那首先了解SVG的含义. 可伸缩矢量图形(Scalable Vector Graphics) 定义用于网络的基于矢量的图形 使用XML格式定义图形 图像在放大或改变尺寸的情况下其图形质量不会有所损失 万维网联盟的标准 与诸如DOM和XSL之类的W3C标准是一个整体 SVG在Web上的应用非常广泛,在Android 5.X之前的Android版本上,可以通过一些第三方开源库来在And

Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用

在上一篇文章Android IPC机制(二)用Messenger进行进程间通信中我们介绍了使用Messenger来进行进程间通信的方法,但是我们能发现Messenger是以串行的方式来处理客户端发来的信息,如果有大量的消息发到服务端,服务端仍然一个一个的处理再响应客户端显然是不合适的.另外,Messenger用来进程间进行数据传递但是却不能满足跨进程的方法调用,接下来我们来使用AIDL来实现跨进程方法调用,此前我们都是用Eclipse来实现的,这次我们看看在Android Studio中使用AI