Android自定义view-渐变色,着色器等主要高级效果全解析

3,667 阅读5分钟

本文的目的

本文目的在于一篇文章讲明白自定义view中的各种复杂炫酷的核心,希望大家看完此文章以后拿到设计师的ui效果能一下心里 有谱。知道大概用什么api可以做到什么事,哪些能做,哪些不能做。实际上百分之90的设计师给的方案都可以从这些api中 找到解决方案。

颜色渐变Gradient

  Shader shader = new LinearGradient(100, 100, 500, 500, Color.parseColor("#007500"),
                    Color.parseColor("#ff3333"), Shader.TileMode.REPEAT);
            paint.setShader(shader);
            canvas.drawRect(100, 100, 500, 500, paint);

看下效果:

下面修改一下代码看看三种TileMode的不同。

 //只有你Gradient的两点在 你绘制范围之内的时候才能见到三种不同 Shader.TileMode的效果
        Shader shader = new LinearGradient(200, 200, 400, 400, Color.parseColor("#007500"),
                Color.parseColor("#ff3333"), Shader.TileMode.REPEAT);
        paint.setShader(shader);
        canvas.drawRect(100, 100, 500, 500, paint);


        Shader shader2 = new LinearGradient(200, 700, 400, 900, Color.parseColor("#007500"),
                Color.parseColor("#ff3333"), Shader.TileMode.CLAMP);
        paint.setShader(shader2);
        canvas.drawRect(100, 600, 500, 1000, paint);


        Shader shader3 = new LinearGradient(200, 1200, 400, 1300, Color.parseColor("#007500"),
                Color.parseColor("#ff3333"), Shader.TileMode.MIRROR);
        paint.setShader(shader3);
        canvas.drawRect(100, 1100, 500, 1500, paint);

类似的还有RadialGradient SweepGradient 大家可以自行搜索效果,这里不过多介绍了,大同小异。理解了绘制范围你就能真正 理解三种tileMode的含义和效果了

BitmapShader着色器

要精确理解shader 其实也要熟悉坐标系,一切的一切都和坐标和绘制区域息息相关,效果理解是其次,主要要精确理解坐标和 绘制区域。首先我在xxhdpi的目录下放了一张美女图片

然后我们写一段代码在展示这张图片到我的手机里,我的手机当然也是xxhdpi的手机,这样图片不会缩放,我们理解起来容易

 Paint mPaint = new Paint();

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.dly);

        canvas.drawBitmap(bitmap, 100, 50, mPaint);

        canvas.drawBitmap(bitmap, 100, 610, mPaint);


//        Shader shader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
//        mPaint.setShader(shader);
//
//        canvas.drawCircle(500, 500, 500, mPaint);
    }

看下效果

然后再来理解shader

 Paint mPaint = new Paint();

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.dly);
        //绘制
        canvas.drawBitmap(bitmap, 0, 0, mPaint);


        Shader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        mPaint.setShader(shader);

        canvas.drawCircle(900, 900, 100, mPaint);
    }

好,现在我们说 只想看到丫丫的脸,不想看其他的,哪我们既然知道丫丫的bitmap绘制区域是在view的左上角, 宽高我们也知道 是366和550,所以大概估算一下, 我们圆心的位置 180,250,半径就是150左右

 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.dly);
        //绘制
      //  canvas.drawBitmap(bitmap, 0, 0, mPaint);


        Shader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        mPaint.setShader(shader);

        canvas.drawCircle(180, 250, 150, mPaint);

嗯效果基本满足,然后我们再想看看tilemode的效果怎么办?其实这里的tilemode效果和上面讲的渐变的tilemode是一样的, 超出原本的bitmap绘制区域 才能看到效果噢,所以我们把半径放大就可以看到tilemode的效果了

 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.dly);
        //绘制
      //  canvas.drawBitmap(bitmap, 0, 0, mPaint);


        Shader shader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        mPaint.setShader(shader);

        canvas.drawCircle(180, 250, 450, mPaint);

ComposeShader混合着色器

这个稍微有点难度和负责,主要是理解方式要找准。找准理解方式,其实就简单多了。

 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.dly);
        //绘制
      //  canvas.drawBitmap(bitmap, 0, 0, mPaint);


        Shader shader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        mPaint.setShader(shader);

        canvas.drawCircle(183, 275, 183, mPaint);

然后绘制一个圆形 注意坐标是怎么算出来的啊,其实就是算这个矩形的中心点。不说了,很显然最终效果应该是在屏幕中间有个圆形的头像

  Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.dly);
        Shader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

        //这个android logo的原图是 144*144大小 注意了
        Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher_round);
        Shader shader2 = new BitmapShader(bitmap2, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

        Shader composeShader = new ComposeShader(shader, shader2, PorterDuff.Mode.SRC_OVER);

        mPaint.setShader(composeShader);

        canvas.drawCircle(102, 72, 72, mPaint);

然后看下这个混合着色器的效果:

改变不一样的参数 有不一样的效果。

//这个参数就是蒙版抠图效果了
        Shader composeShader = new ComposeShader(shader, shader2, PorterDuff.Mode.DST_IN);

PorterDuff.Mode 参数很多,具体的可以查看官方文档的大图,好好理解是什么效果,这里只给一种效果 让大家明白如何使用。其实最后还是要理解坐标系。

ColorFilter 颜色变换

这个好理解,其实改变画笔的颜色矩阵,从而有不一样的效果。比如阳光色啊之类的。具体的有 LightingColorFilter PorterDuffColorFilter ColorMatrixColorFilter等。

取个最简单的LightingColorFilter 模拟下光照效果 看看

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.dly);
//        Shader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

//        //这个android logo的原图是 144*144大小 注意了
//        Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher_round);
//        Shader shader2 = new BitmapShader(bitmap2, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//
//        //这个参数就是蒙版抠图效果了
//        Shader composeShader = new ComposeShader(shader, shader2, PorterDuff.Mode.DST_IN);


        ColorFilter lightingColorFilter = new LightingColorFilter(0x00ffff, 0x000000);
        mPaint.setColorFilter(lightingColorFilter);

        canvas.drawBitmap(bitmap,0,0,mPaint);

MaskFilter

 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.dly);

        //BlurMaskFilter.Blur参数值大家可以自行试试 总共四种
        mPaint.setMaskFilter(new BlurMaskFilter(50, BlurMaskFilter.Blur.INNER));

        canvas.drawBitmap(bitmap,0,0,mPaint);

至此,大部分自定义view的基础效果就基本讲完了,最终我们常用的自定义view方法不过是在这个基础上,加上绘制文字,几何变换,属性动画而已。这一部分可以自己写demo好好理解下,后续的文字效果,几何变化,view裁切,属性动画都是以这部分内容为基础。