安卓自定义view-Paint 画笔

咱们在学画画的时候,首先须要买画笔,而后再购买画板,就能够愉快的创做(瞎画😄)啦!在安卓中也是这么设计的,毕竟设计也是要符合实际生活规律的。OK,那么本篇就是从画笔的基础开始讲起。android

画笔基础 api

  1. 构造方法
// 建立出一只画笔🖌️
Paint paint = new Paint()
复制代码
  1. 画笔的颜色,选择喜欢的颜色做画, 姿式不少,选择喜欢的便可
mPaint.setColor(Color.parseColor("#ff00ff"));

mPaint.setColor(Color.argb(200, 200, 200,200));

// 或 setARGB 的方式
mPaint.setARGB(200, 200, 200, 200);

复制代码
  1. 画笔的尺寸大小,粗细程度, 好比我就喜欢粗的笔
mPaint.setStrokeWidth(30);
复制代码
  1. 画笔的样式,比实际动手画画方便一些,好比咱们画一个圆要填充整个圆,是否是得一点点的涂。可是手机上画能够一下搞定。这里有三个模式: STROKE(画线)、FILL(填充)、FILL_AND_STROKE (轮廓和填充)
// 有三种模式: FILL、STROKE、FILL_AND_STROKE
mPaint.setStyle(Paint.Style.STROKE);
复制代码
  1. 抗锯齿 在计算机图形学中产生图形锯齿是由于计算机屏幕中的点是离散的有大小的像素点来表示的,当咱们用计算机画连续的曲线的时候因为屏幕像素没有办法表示小数坐标,而后用四舍五入后整数来表示,放大来看,就会出现锯齿。通常解决办法就是找一些次要的像素填充或者透明度填充(经过计算后的到边缘的颜色值)
mPaint.setAntiAlias(true);
复制代码

来吧!看看效果左边未设置抗锯齿,右边设置了抗锯齿。git

抗锯齿图片对比

你是否是想说,这不同吗?这么看确实看不出区别。毕竟是人的肉眼,确定没办法观察到很细节的东西。那么咱们经过放大后再观察一下:github

抗锯齿放大后

这下你看到了吧!未开启抗锯齿算法优化的化,能够看到边缘有锯齿形状。而右侧的就更加平顺一些。那咱们有没有必要开启抗锯齿呢? 其实大部分状况下是能够不开启的,由于如今的手机分辨越来约高,只要不是对精细度追求极致的化,是能够不用开启的。算法

  1. 图像抖动 因为如今的手机分辨率愈来愈高,而且色彩深度默认为 32 位,效果也足够清晰了。抖动”是印刷行业和出版业中经常使用的一种工艺,老式的针式打印机只能打印出来黑点和白点,但是黑白图片是有灰度级的,那么如何打印出来图片呢?“抖动”由此而生,抖动试图经过在白色背景上生成黑色的二值图像来给出色调变化的直观印象,能够假想一下,黑点越密,那么远距离观察就越黑,如何控制黑点的分布就是“抖动”算法的核心。

若是颗粒越小,是否是就会看起来是灰色啦,欺骗人的肉眼。 canvas

  1. 图形端的形状 固然这是本身的术语描述,应该叫什么不清楚。共有三种:ROUND、BUTT、SQUARE, 第一种好理解圆的形状,第三种也好理解矩形形状,第二种是什么鬼。咱们看下效果图。
mPaint.setStrokeCap(Paint.Cap.ROUND);
复制代码

  1. 线段之间拐角形状

线段与线段之间的形状,可让线段的链接看来更加圆润仍是棱角分明。它也有三种: MITER、ROUND、BEVEL。从单词的的翻译来看,彷佛只知道 ROUND,直接来看效果图。api

mPaint.setStrokeJoin(Paint.Join.ROUND);

复制代码

仔细看的化能看出在四个顶点都有些不一样,MITER 更加尖一些, ROUND 则圆润一些,BEVEL 好像是切了一个角而来。ROUND 很好理解,说一些 MITER 和 BEVEl。它们二者都有类似之处,就是线段的末尾进行延长相交,对于 MITER 来讲若是线段之间的夹角越小,那么顶点就会越尖。再来看一张图,这张图就比刚才矩形更加直观了。数组

  1. set(src)

这个很简单,就是从某一个建立好的画笔复制一份使用bash

  1. setFlags
等同于分别设置 setAntiAlias(true) 和 setDither(true)
mPaint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
复制代码

高阶部分 api

设置颜色

  1. setShader(Shader shader) 设置 Shader

翻译过来称为着色器,其实也是用来设置画笔的颜色的。它有 5 个子类,依次是: LinearGradient、SweepGradient、RadialGradient、BitmapShader、ComposeShaderide

. LinearGradient 从名字能够看出是线性渐变的意思,直接上效果图优化

LinearGradient linearGradient = new LinearGradient(
                300,
                300,
                700,
                700,
                Color.RED,
                Color.YELLOW,
                Shader.TileMode.CLAMP);
        mPaint.setShader(linearGradient);

复制代码

能够看到接近最后一个点的地方设置成了蓝色,这几个参数的意思是: 起点的 x, y 左边,终点的 x,y 坐标,起点的颜色,终点的颜色, Shader.TileMode.CLAMP 在两个端点以外延用端点的颜色。

再来看看其余的用法,稳住别慌,坚持住。

LinearGradient linearGradient = new LinearGradient(
                300,
                300,
                900,
                900,
                new int[] {
                        Color.RED,
                        Color.BLUE
                },
                new float[] {
                  0.5f,
                  0.5f
                },
                Shader.TileMode.CLAMP);
        mPaint.setShader(linearGradient);

复制代码

这回发现多了两个参数,其实并无,只是用int[] 添加颜色,float[] 数组用来指定端点到某一个区域的填充的颜色,须要和前面的 int[] 数组长度一直,不然会 gg。

继续来看 TileMode 的三种区别

MIRROR:

REPEAT

. SweepGradient 渐变,扇形渐变,也就是说某一个角度扫过的区域的渐变效果,能够看到跟线性渐变的使用方式差很少。

SweepGradient sweepGradient = new SweepGradient(300, 300,
                new int[]{Color.RED, Color.GREEN, Color.BLUE},
                new float[]{
                     0.1f, 0.3f, 1.0f
                });

        mPaint.setShader(sweepGradient);
复制代码

. RadialGradient 径向渐变,根据圆的半径进行渐变色

RadialGradient radialGradient = new RadialGradient(700, 700, 300,
                new int[]{Color.RED, Color.GREEN, Color.BLUE},
                new float[]{0.2f, 0.5f, 1.0f},
                Shader.TileMode.CLAMP);

        mPaint.setShader(radialGradient);
复制代码

效果图

它也有三种使用模式,其实跟线性渐变同样。

RadialGradient radialGradient = new RadialGradient(700, 700, 100,
                new int[]{Color.RED, Color.GREEN, Color.BLUE},
                new float[]{0.2f, 0.5f, 1.0f},
                Shader.TileMode.CLAMP); // 更换模式.
复制代码

REPEAT:

RadialGradient radialGradient = new RadialGradient(700, 700, 150,
                Color.RED, Color.BLUE, Shader.TileMode.MIRROR);

        mPaint.setShader(radialGradient);
复制代码

MIRROR:

. BitmapShader. Bitmap 是图片,哟,图片也能够处理的哦!

BitmapShader bitmapShader = new BitmapShader(mBitmap,
                Shader.TileMode.REPEAT,
                Shader.TileMode.MIRROR);
        mPaint.setShader(bitmapShader);
        
      
// 绘制一个矩形,填充 view 的宽高.
canvas.drawRect(0, 0, mWidth, mHeight, mPaint);
复制代码

CLAMP: 以图片的 x 轴的最后一个像素填充 x 轴和 y 轴

REPEAT:以相同的图片进行填充.

MIRROR: 镜像的方式填充,比如照镜子同样,原像和镜像恰好相反

当前你也能够混合,好比在 x 轴方向重复填充, y 轴方向镜像填充

还能够这样:将图片填充到圆里

. ComposeShader 使用组合的方式,将前面的着色器配合来使用.

// 关闭硬件加速
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.beauty);

        mHeartBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.heart);

        BitmapShader bitmapShader1 = new BitmapShader(mBitmap,
                Shader.TileMode.CLAMP,
                Shader.TileMode.CLAMP);

        BitmapShader bitmapShader2 = new BitmapShader(mHeartBitmap,
                Shader.TileMode.CLAMP,
                Shader.TileMode.CLAMP);

        ComposeShader composeShader = new ComposeShader(bitmapShader1, bitmapShader2, PorterDuff.Mode.SRC_OUT);
        mPaint.setShader(composeShader);

        
   canvas.drawCircle(500, 500, 300, mPaint);

复制代码
  1. setColorFilter(ColorFilter colorFilter)

从名字就能够看出,主要是对颜色进行过滤。就是在绘制以前先过滤点指定的颜色再进行显示。也有三个子类供咱们使用: LightingColorFilter、PorterDuffColorFilter、ColorMatrixColorFilter

. LightingColorFilter 光照过滤,就比如咱们带着太阳镜过滤了太阳光中对眼睛有伤害的紫外线,而后眼睛看到了就是比较柔的。学到老活到老, 来看使用

LightingColorFilter colorFilter = new LightingColorFilter(
                Color.parseColor("#ffffff"),
                Color.parseColor("#000000"));
        mPaint.setColorFilter(colorFilter);
复制代码

经过看源码,能够看到它的计算规则是:

R' = R * colorMultiply.R + colorAdd.R G' = G * colorMultiply.G + colorAdd.G
 B' = B * colorMultiply.B + colorAdd.B 复制代码

不难发现,若是采起上面的方式设置 mul: #ffffff, add: #000000, 那么计算以下:

R' = R * 0xff/0xff + 0x00 G' = G * 0xff/0xff + 0x00
B' = B * 0xff/0xff + 0x00 结果和原图同样. 复制代码

如今若是要去掉原图中红色,只要将 mul 改成 0x00ffff, add 不变

LightingColorFilter colorFilter = new LightingColorFilter(
                Color.parseColor("#00ffff"),
                Color.parseColor("#000000"));
        mPaint.setColorFilter(colorFilter);
复制代码

加强红色:mul 不变, add: 0x660000

LightingColorFilter colorFilter = new LightingColorFilter(
                Color.parseColor("#ffffff"),
                Color.parseColor("#660000"));
        mPaint.setColorFilter(colorFilter);
复制代码

. PorterDuffColorFilter

指定一种颜色为源,采用图形混合模式进行处理。

// 指定白色为源,混合模式为目标显示 DST_OVER
 PorterDuffColorFilter porterDuffColorFilter = new PorterDuffColorFilter(
                Color.parseColor("#ffffff"),
                PorterDuff.Mode.DST_OVER);
        mPaint.setColorFilter(porterDuffColorFilter);
复制代码

更改原图颜色,并更改混合模式: SRC_OVER

PorterDuffColorFilter porterDuffColorFilter = new PorterDuffColorFilter(
                Color.parseColor("#ED29AB"),
                PorterDuff.Mode.SRC_OVER);
        mPaint.setColorFilter(porterDuffColorFilter);

复制代码

. ColorMatrixColorFilter

这个就比较凶残了,由于是在是很是强大,内部是一个 4x5 的矩阵,经过变化矩阵中各通道的值,能够实现各类效果。

[ a, b, c, d, e,
  f, g, h, i, j,
  k, l, m, n, o,
  p, q, r, s, t ]

复制代码

经过代码搞搞事情,看看能玩出什么花招出来。

float[] colorMatrix = new float[] {
                1, 0, 0, 0, 0,
                0, 1, 0, 0, 0,
                0, 0, 1, 0, 0,
                0, 0, 0, 1, 0
        };
ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix);
mPaint.setColorFilter(colorMatrixColorFilter);

复制代码

是否是没啥变化,由于上面矩阵定义的是白色的矩阵。因此没啥变化。再来看下面的矩阵会有什么变化。

float[] colorMatrix = new float[] {
                0.33F, 0.59F, 0.11F, 0, 0,
                0.33F, 0.59F, 0.11F, 0, 0,
                0.33F, 0.59F, 0.11F, 0, 0,
                0, 0, 0, 1, 0,
        };
        ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix);
        mPaint.setColorFilter(colorMatrixColorFilter);
        
ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(TECHNICOLOR);
mPaint.setColorFilter(colorMatrixColorFilter);
复制代码

是否是发现变灰色了。继续修改

private static final float[] INVERT = new float[] {
            -1, 0, 0, 0, 255,
            0, -1, 0, 0, 255,
            0, 0, -1, 0, 255,
            0, 0, 0, 1, 0,
    };
    
ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(TECHNICOLOR);
mPaint.setColorFilter(colorMatrixColorFilter);
复制代码

是否是有点想胶卷的的图像颜色啦😄,继续修改。

private static final float[] RGB_TO_BGR = new float[] {
            0, 0, 1, 0, 0,
            0, 1, 0, 0, 0,
            1, 0, 0, 0, 0,
            0, 0, 0, 1, 0,
    };
ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(TECHNICOLOR);
mPaint.setColorFilter(colorMatrixColorFilter);
复制代码

能够看到这里将 RGB 转为 BGR 的方式显示效果。

在看,如何设置称棕褐色。

private static final float[] SEPIA = new float[] {
            0.393F, 0.769F, 0.189F, 0, 0,
            0.349F, 0.686F, 0.168F, 0, 0,
            0.272F, 0.534F, 0.131F, 0, 0,
            0, 0, 0, 1, 0,
    };
    
ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(TECHNICOLOR);
mPaint.setColorFilter(colorMatrixColorFilter);
复制代码

黑白照片

private static final float[] BLACK_AND_WHITE = new float[] {
            1.5F, 1.5F, 1.5F, 0, -255,
            1.5F, 1.5F, 1.5F, 0, -255,
            1.5F, 1.5F, 1.5F, 0, -255,
            0, 0, 0, 1, 0,
    };
    
ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(TECHNICOLOR);
mPaint.setColorFilter(colorMatrixColorFilter);

复制代码

彩色照片

private static final float[] TECHNICOLOR = new float[] {
            1.9125277891456083F, -0.8545344976951645F, -0.09155508482755585F, 0, 11.793603434377337F,
            -0.3087833385928097F, 1.7658908555458428F, -0.10601743074722245F, 0, -70.35205161461398F,
            -0.231103377548616F, -0.7501899197440212F, 1.847597816108189F, 0, 30.950940869491138F,
            0, 0, 0, 1, 0
    };
    
ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(TECHNICOLOR);
mPaint.setColorFilter(colorMatrixColorFilter);
复制代码

经过不一样的矩阵,能够获得不一样转换后的效果。

接下来讨论一下滤镜的原理,安卓系统使用一个颜色矩阵 ColorMatrix(下图 A 表示) 来处理图像的颜色,对于每个像素点都有一个颜色份量矩阵来保存的 RGBA 值(下图 C)。

来看官方文档的描述 ColorMatrix, 在安卓系统中经过维护一个 4 * 5 的觉得数组来表示。那对一个颜色的运算使用矩阵乘法以下:

// 运算的结果为 R'G'B'A'
   R’ = a*R + b*G + c*B + d*A + e;
   G’ = f*R + g*G + h*B + i*A + j;
   B’ = k*R + l*G + m*B + n*A + o;
   A’ = p*R + q*G + r*B + s*A + t;
复制代码

这个公式告诉咱们,第一行 abcde 决定红色,第二行 fghij 决定绿色,第三行 klmno 决定蓝色,最后一行 pqrst 决定透明度。

再根据文档中可看出 reset 方法的颜色矩阵为:

[ 1 0 0 0 0   - red vector
   0 1 0 0 0   - green vector
   0 0 1 0 0   - blue vector
   0 0 0 1 0 ] - alpha vector
复制代码

这个颜色也称为初始颜色,运用这个颜色矩阵做用在图片的话,将会保持原来的颜色不变。下面来一个简单的变化,矩阵取反,其实前面已经看到过效果了。

[ -1 0 0 0 255   - red vector
   0 1- 0 0 255   - green vector
   0 0 -1 0 255   - blue vector
   0 0 0 -1 0 ] - alpha vector

复制代码

这里的最后一列,表示的是颜色的偏移量,下面的意思就是红色和绿色偏移100。

[1,0,0,0,100,
 0,1,0,0,100,
 0,0,1,0,100,
 0,0,0,1,0
]

复制代码

下面是我写的一个能够经过设置参数来调节图片的颜色过滤,代码很简单安卓碎片知识

若是开发中是这样设置的话,我相信可能会累死。安卓 ColorMatrix 颜色矩阵中封装了一些 API 来快速调整上面这三个颜色参数,能够方便的使用。

. 色调

对色彩进行旋转运算,至于如何旋转的这个我也没有研究。感兴趣的能够上网找找资料看看。

// 第一个参数: 0 表示绕红色的旋转, 1 表示绕绿色的旋转, 2表示绕蓝色的旋转
// 至于第二个参数:能够经过源码大概能够知道。它应该在 -180 ~ 180 之间。
colorMatrix.setRotate(0, 180);
colorMatrix.setRotate(1, 180);
colorMatrix.setRotate(2, 180);

复制代码
/**
     * Set the rotation on a color axis by the specified values.
     * <p>
     * <code>axis=0</code> correspond to a rotation around the RED color
     * <code>axis=1</code> correspond to a rotation around the GREEN color
     * <code>axis=2</code> correspond to a rotation around the BLUE color
     * </p>
     */
    public void setRotate(int axis, float degrees) {
        reset();
        // 周期是在 -180 到 180 之间
        double radians = degrees * Math.PI / 180d;
        float cosine = (float) Math.cos(radians);
        float sine = (float) Math.sin(radians);
        switch (axis) {
        // Rotation around the red color
        case 0:
            mArray[6] = mArray[12] = cosine;
            mArray[7] = sine;
            mArray[11] = -sine;
            break;
        // Rotation around the green color
        case 1:
            mArray[0] = mArray[12] = cosine;
            mArray[2] = -sine;
            mArray[10] = sine;
            break;
        // Rotation around the blue color
        case 2:
            mArray[0] = mArray[6] = cosine;
            mArray[1] = sine;
            mArray[5] = -sine;
            break;
        default:
            throw new RuntimeException();
        }
    }
复制代码

. 饱和度 放大色彩饱和度,大于 1 色彩开始饱和,等于 1 不变,小于 1 过分到没有色彩饱和,等于 0 为黑白图像。

colorMatrix1.setSaturation(3.0f);

复制代码

就是将 RBGA 加上咱们指定的 sat。

/**
     * Set the matrix to affect the saturation of colors.
     *
     * @param sat A value of 0 maps the color to gray-scale. 1 is identity.
     */
    public void setSaturation(float sat) {
        reset();
        float[] m = mArray;

        final float invSat = 1 - sat;
        final float R = 0.213f * invSat;
        final float G = 0.715f * invSat;
        final float B = 0.072f * invSat;

        m[0] = R + sat; m[1] = G;       m[2] = B;
        m[5] = R;       m[6] = G + sat; m[7] = B;
        m[10] = R;      m[11] = G;      m[12] = B + sat;
    }
复制代码

. 亮度 以不一样的颜色比列进行混合, 若是所有为 0 为黑色。所有为 1 则为图片原有颜色。若是看源码的话,能够看到它其实设置的是 a[0],a[6],a[12], a[18] 的值,而后其余的都置为0。

colorMatrix1.setScale(0f, 0f, 0f,1.0f);

复制代码
/**
     * Set this colormatrix to scale by the specified values.
     */
    public void setScale(float rScale, float gScale, float bScale,
                         float aScale) {
        final float[] a = mArray;

        for (int i = 19; i > 0; --i) {
            a[i] = 0;
        }
        a[0] = rScale;
        a[6] = gScale;
        a[12] = bScale;
        a[18] = aScale;
    }

复制代码

好吧! 写到这儿就结束了。可是 Paint 的尚未说完,剩下的还有跟文字相关的、其余的图层混合模式。

相关文章
相关标签/搜索