自定义控件其实很简单(笔记一)

目录

  • [Paint 之 Font](#Paint 之 Font)

姿势点

  • 在Android中设置数字类型的参数时如果没有特别的说明,参数的单位一般都为px像素。
  • HardwareAccel 查看那些方法不支持硬件加速;
  • 关闭硬件加速,AndroidManifest.xml==> application节点下android:hardwareAccelerated=false
  • 针对某个View关闭硬件加速 setLayerType(LAYER_TYPE_SOFTWARE, null);
  • Android会把拷贝到资源目录的图片转为RGB565

ColorFilter


** ColorMatrix**

ColorMatrix colorMatrix = new ColorMatrix(new float[]{
        1, 0, 0, 0, 0,
        0, 1, 0, 0, 0,
        0, 0, 1, 0, 0,
        0, 0, 0, 1, 0,
});
  • 行表示的R(红色)的向量,第二行表示的G(绿色)的向量,第三行表示的B(蓝色)的向量,最后一行表示A(透明度)的向量
  • 这个矩阵不同的位置表示的RGBA值,其范围在0.0F至2.0F之间,1为保持原图的RGB值;
  • 每一行的第五列数字表示偏移值

Usage

// 生成色彩矩阵
ColorMatrix colorMatrix = new ColorMatrix(new float[]{
        0.5F, 0, 0, 0, 0, /*1表示不变颜色*/
        0, 0.5F, 0, 0, 0,
        0, 0, 0.5F, 0, 0,
        0, 0, 0, 1, 0,
});
mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));

数学原理是:

ColorMatrix

实践效果

原图

Demo1

// 生成色彩矩阵
ColorMatrix colorMatrix = new ColorMatrix(new float[]{
        0.5F, 0, 0, 0, 0,
        0, 0.5F, 0, 0, 0,
        0, 0, 0.5F, 0, 0,
        0, 0, 0, 1, 0,
});
mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
Demo1

Demo2

ColorMatrix colorMatrix = new 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,
});
Demo2

Demo3

ColorMatrix colorMatrix = new ColorMatrix(new float[]{
        -1, 0, 0, 1, 1,
        0, -1, 0, 1, 1,
        0, 0, -1, 1, 1,
        0, 0, 0, 1, 0,
});
Demo3

Demo4

ColorMatrix colorMatrix = new ColorMatrix(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,
});
Demo4

Demo5 红色的变成了蓝色而蓝色的就变成了红色

ColorMatrix colorMatrix = new ColorMatrix(new float[]{
        0, 0, 1, 0, 0,
        0, 1, 0, 0, 0,
        1, 0, 0, 0, 0,
        0, 0, 0, 1, 0,
});
Demo5

LightingColorFilter
A color filter that can be used to simulate simple lighting effects.
[LightingColorFilter](http://developer.android.com/reference/android/graphics/LightingColorFilter.html#LightingColorFilter(int, int)) (int mul, int add)

R' = R * colorMultiply.R + colorAdd.R 
G' = G * colorMultiply.G + colorAdd.G 
B' = B * colorMultiply.B + colorAdd.B

usage:

//设置颜色过滤后为黄色  
mPaint.setColorFilter(new LightingColorFilter(0xFFFFFFFF, 0X00FFFF00));  
 mPaint.setColorFilter(null);   //还原本色  

PorterDuffColorFilter

混合模式

Paint.setXfermode(Xfermode xfermode)

Xfermode

AvoidXfermode

  • API 16 以上机型必须在应用或手机上关闭硬件加速 ;
  • AvoidXfermode(int opColor, int tolerance, AvoidXfermode.Mode mode)
    opColor:表示一个16进制的可以带透明通道的颜色值例如0x12345678
    Tolerance:表示容差值,那么什么是容差呢?你可以理解为一个可以标识“精确”或“模糊”的东西
    AvoidXfermode.Mode: AvoidXfermode.Mode.AVOID或者AvoidXfermode.Mode.TARGET

AvoidXfermode.Mode.TARGET
在该模式下Android会判断画布上的颜色是否会有跟opColor不一样的颜色,比如我opColor是红色,那么在TARGET模式下就会去判断我们的画布上是否有存在红色的地方,如果有,则把该区域“染”上一层我们画笔定义的颜色,否则不“染”色,而tolerance容差值则表示画布上的像素和我们定义的红色之间的差别该是多少的时候才去“染”的,比如当前画布有一个像素的色值是(200, 20, 13),而我们的红色值为(255, 0, 0),当tolerance容差值为255时,即便(200, 20, 13)并不等于红色值也会被“染”色,容差值越大“染”色范围越广反之则反.
Usage:

 avoidXfermode = new AvoidXfermode(0XFFFFFFFF, 0, AvoidXfermode.Mode.TARGET);  
mPaint.setXfermode(avoidXfermode);  

API 16+没有关闭硬件加速


API 16以下关闭硬件加速



符合条件的效果,在我们的模式为TARGET容差值为0的时候此时只有当图片中像色颜色值为0XFFFFFFFF的地方才会被染色,而其他地方不会有改变,而当容差值为255的时候只要是跟0XFFFFFFFF有点接近的地方都会被染色。


Paste_Image.png

AvoidXfermode.Mode.AVOID
AVOID是我们指定的颜色是否与画布不一样
AvoidXfermode(0XFFFFFFFF, 0, AvoidXfermode.Mode.AVOID):


当模式为AVOID容差值为0时,只有当图片中像素颜色值与0XFFFFFFFF完全不一样的地方才会被染色
AvoidXfermode(0XFFFFFFFF, 255, AvoidXfermode.Mode.AVOID):


当容差值为255时,只要与0XFFFFFFFF稍微有点不一样的地方就会被染色
那么这玩意究竟有什么用呢?比如说当我们只想在白色的区域画点东西或者想把白色区域的地方替换为另一张图片的时候就可以采取这种方式!

PixelXorXfermode

  • API 16以过时
  • useless

PorterDuffXfermode

  • 概念最早来自于SIGGRAPH的Tomas Proter和Tom Duff
  • 混合图形的概念极大地推动了图形图像学的发展,延伸到计算机图形图像学像Adobe和AutoDesk公司著名的多款设计软件都可以说一定程度上受到影响.

    Usage:
 paint.setXfermode( new PorterDuffXfermode(PorterDuff.Mode.SCREEN);
  • Sa全称为Source alpha表示源图的Alpha通道;Sc全称为Source color表示源图的颜色;Da全称为Destination alpha表示目标图的Alpha通道;Dc全称为Destination color表示目标图的颜色。
  • “[……]”里分为两部分 , 前代表计算后的Alpha通道,后代表计算后的颜色值。
  • More Alpha compositing wiki

PorterDuffXfermode高级姿势

  • PorterDuff.Mode.ADD 计算方式:Saturate(S + D);Chinese:饱和相加


  • PorterDuff.Mode.CLEAR
  • PorterDuff.Mode.DARKEN
    计算方式:[Sa + Da - SaDa, Sc(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)];Chinese:变暗

两个图像混合,较深的颜色总是会覆盖较浅的颜色,如果两者深浅相同则混合,如图,黄色覆盖了红色而蓝色和青色因为是跟透明混合所以不变.

  • PorterDuff.Mode.DST_IN

两张图合成去掉美女头上的字

    
         // 先绘制dis目标图
        canvas.drawBitmap(bitmapDis, x, y, mPaint);
        // 设置混合模式
        mPaint.setXfermode(PorterDuff.Mode.DST_IN);
        // 再绘制src源图
        canvas.drawBitmap(bitmapSrc/*黑色底图*/, x, y, mPaint);
        // 还原混合模式
        mPaint.setXfermode(null);
        // 还原画布
        canvas.restoreToCount(sc);
  • PorterDuff.Mode.DST_OUT
    计算方式:[Da * (1 - Sa), Dc * (1 - Sa)];Chinese:只在源图像和目标图像不相交的地方绘制目标图像

Paint 之 Font

FontMetrics

Summary
  • Top: 是除了Baseline到字符顶端的距离外还应该包含这些符号的高度,bottom类似 ;
  • Android依然会在绘制文本的时候在文本外层留出一定的边距,这就是为什么top和bottom总会比ascent和descent大一点的原因。而在TextView中我们可以通过xml设置其属性android:includeFontPadding="false"去掉一定的边距值但是不能完全去掉。
            
         mFontMetrics = mPaint.getFontMetrics();
        Log.d("Aige", "ascent:" + mFontMetrics.ascent);
        Log.d("Aige", "top:" + mFontMetrics.top);
        Log.d("Aige", "leading:" + mFontMetrics.leading);
        Log.d("Aige", "descent:" + mFontMetrics.descent);
        Log.d("Aige", "bottom:" + mFontMetrics.bottom);

Log.d()——文字只有一行,故leading为零

StaticLayout结合TextPaint实现换行

mStaticLayout = new StaticLayout(TEXT, mTextPaint, canvas.getWidth(), Alignment.ALIGN_NORMAL, 1.0F, 0.0F, false);  
mStaticLayout.draw(canvas);  

Paint 之方法解析

  • measureForwards表示向前还是向后测量
  • measuredWidth为一个可选项,可以为空,不为空时返回真实的测量值
  • 定义字体 Android中字体有四种样式:BOLD(加粗),BOLD_ITALIC(加粗并倾斜),ITALIC(倾斜),NORMAL(正常);而其为我们提供的字体有五种:DEFAULT,DEFAULT_BOLD,MONOSPACE,SANS_SERIF和SERIF
//系统自带
textPaint.setTypeface(Typeface.create("SERIF", Typeface.NORMAL));
textPaint.setTypeface(Typeface.create(Typeface.SERIF, Typeface.NORMAL));
//自定义字体 一
createFromAsset(AssetManager mgr, String path)
createFromFile(String path)和createFromFile(File path)
//自定义字体 二
// 获取字体并设置画笔字体
Typeface typeface = Typeface.createFromAsset(context.getAssets(), "kt.ttf");
textPaint.setTypeface(typeface);
  • setTextSkewX (float skewX) // 设置画笔文本倾斜 官方推荐-0.25F
  • setStrikeThruText (boolean strikeThruText) //文本删除线
  • setLinearText (boolean linearText)
    设置是否打开线性文本标识,这玩意对大多数人来说都很奇怪不知道这玩意什么意思。想要明白这东西你要先知道文本在Android中是如何进行存储和计算的。在Android中文本的绘制需要使用一个bitmap作为单个字符的缓存,既然是缓存必定要使用一定的空间,我们可以通过setLinearText (true)告诉Android我们不需要这样的文本缓存。
  • setFakeBoldText (boolean fakeBoldText) //设置文本仿粗体

** setMaskFilter(MaskFilter maskfilter) **

  • 关闭硬件加速 setLayerType(LAYER_TYPE_SOFTWARE, null);

BlurMaskFilter
BlurMaskFilter(float radius, BlurMaskFilter.Blur style) 是根据Alpha通道的边界来计算模糊的

mPaint.setMaskFilter(new BlurMaskFilter(20, BlurMaskFilter.Blur.SOLID));

SOLID: 在图像的Alpha边界外产生一层与Paint颜色一致的阴影效果而不影响图像本身,而NORMAL,OUTER和INNER,NORMAL会将整个图像模糊掉:

而NORMAL会在Alpha边界外产生一层阴影且会将原本的图像变透明
OUTER会在Alpha边界外产生一层阴影且会将原本的图像变透明:
INNER则会在图像内部产生模糊:
  • INNER效果其实并不理想,实际应用中我们使用的也少,我们往往会使用混合模式和渐变和获得更完美的内阴影效果。
  • 从Bitmap中获取其Alpha通道,在绘制Bitmap
        // 获取位图的Alpha通道图
        shadowBitmap = srcBitmap.extractAlpha();
        // 先绘制阴影
        canvas.drawBitmap(shadowBitmap, x, y, shadowPaint);ps
        // 再绘制位图
        canvas.drawBitmap(srcBitmap, x, y, null);

效果

EmbossMaskFilter让你绘制的图像感觉像是从屏幕中“凸”起来更有立体感一样(在设计软件中类似的效果称之为斜面浮雕)。
public EmbossMaskFilter (float[] direction, float ambient, float specular, float blurRadius)

  • direction 指光照方向,有而且只能有三个值即float[x,y,z],
    首先x和y很好理解,平面的两个维度,上面小球使用[1,1] 45度角, 而z轴表示光源是在屏幕后方还是屏幕前方,上面我们是用的是1,正值表示光源往屏幕外偏移1个单位,负值表示往屏幕里面偏移。[x,y,z]表示的是空间坐标,代表光源的位置,那么一旦这个位置确定,[ax,ay,az]则没有意义
  • ambient 用来设置环境光的,在Android中环境光默认为白色,其值越大, 球阴影越浅
  • specular是跟高光有关的,其值是个双向值越小或越大高光越强中间值则是最弱

setPathEffect(PathEffect effect)

PathEffect
六个子类分别可以实现不同的路径效果
  • ComposePathEffect(PathEffect outerpe, PathEffect innerpe)会先将路径变成innerpe的效果,再去复合outerpe的路径效果,即:outerpe(innerpe(Path));而SumPathEffect(PathEffect first, PathEffect second)则会把两种路径效果加起来再作用于路径

setShadowLayer(float radius, float dx, float dy, int shadowColor)

  • 该方法为我们绘制的图形添加一个阴影层效果
  • 同样不支持HW
/**
     * 初始化画笔
     */
    private void initPaint() {
        // 实例化画笔
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Style.FILL);
        mPaint.setShadowLayer(10, 3, 3, Color.DKGRAY);
    }
setShadowLayer

Shader

Shader

BitmapShader
[BitmapShader](http://developer.android.com/reference/android/graphics/BitmapShader.html#BitmapShader(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode))(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)
Usage:
mPaint.setShader(new BitmapShader(bitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR));
Shader.TileMode

BitmapShader(bitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)
BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.MIRROR)
  • 着色是先在Y轴拉伸了然后再沿着X轴重复对

LinearGradient

  • mPaint.setShader(new LinearGradient(left, top, right - RECT_SIZE, bottom - RECT_SIZE, Color.RED, Color.YELLOW, Shader.TileMode.REPEAT)); //(left,top) 起点坐标, (right-RECT_SIZE, bottom-RECT_SIZE) 终点坐标,效果如下:


  • LinearGradient(left, top, right, bottom, new int[] { Color.RED, Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE }, new float[] { 0, 0.1F, 0.5F, 0.7F, 0.8F }, Shader.TileMode.MIRROR),效果如下图:
  • new int[](colors[]) 定义渐变颜色
  • new float[](positions[]) 定义渐变区间,取值区间[0, 100%],意思是整个渐变占渐变区域的百分比
positions[] =null, 各颜色均分

应用案例图片1——倒影:

reflect
        // 实例化一个矩阵对象
        Matrix matrix = new Matrix();
        matrix.setScale(1F, -1F);
        // 生成倒影图
        mRefBitmap = Bitmap.createBitmap(mSrcBitmap, 0, 0, mSrcBitmap.getWidth(), mSrcBitmap.getHeight(), matrix, true);
        //生产消失渐变
        mPaint.setShader(
                        new LinearGradient(x, y + mSrcBitmap.getHeight(), x,
                                y + mSrcBitmap.getHeight() + mSrcBitmap.getHeight() / 4,
                                0xAA000000, Color.TRANSPARENT, Shader.TileMode.CLAMP));

    //合成效果
    mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
    
    public void onDraw(Canvas canvas){
        canvas.draw(srcBitmap);
        int sc = canvas.saveLayer(......,null,Canvas.ALL_SAVE_FLAG) ;
        
        canvas.draw(mRefBitmap)
        mPaint.setXfermode(mXfermode) ;
        canvas.drawRect(left,top,right,bottom,mPaint) ;
        mPaint.setXfermode(mXfermode);
        canvas.restoreToCount(sc) ;
    }

应用案例图片2-(去饱和、提亮、色相矫正)
姿势诀窍第一式:


专业术语之梦幻特效
        // 实例化混合模式
        mXfermode = new PorterDuffXfermode(PorterDuff.Mode.SCREEN);
        // 去饱和、提亮、色相矫正
        mBitmapPaint.setColorFilter(new ColorMatrixColorFilter(
                new float[] {
                        0.8587F, 0.2940F, -0.0927F, 0, 6.79F,
                        0.0821F, 0.9145F, 0.0634F, 0, 6.79F,
                        0.2019F, 0.1097F, 0.7483F, 0, 6.79F,
                        0, 0, 0, 1, 0
                }));
    protected void onDraw(Canvas canvas){
        int sc = canvas.saveLayer(...,null,Canvas.ALL_SAVE_FLAG) ;
        canvas.drawColor(0xcc1c093e) ;
        mPaint.setXfermode(mXfermode) ;
        canvas.drawBitmap(bitmap,x,y,mPaint) ;
        mPaint.setXfermode(null) ;
        canvas.restoreToCount(sc) ;
    }

模拟单反相机的暗角效果,压暗图片周围的颜色亮度提亮中心:

姿势诀窍第二式:


RadialGradientPs.png
      onDraw() 最后添加
        // 实例化混合模式
        // 中心颜色为透明而边缘颜色为黑色
        mShaderPaint.setShader(new RadialGradient(screenW / 2, screenH / 2,
                mBitmap.getHeight() * 7 / 8, Color.TRANSPARENT, Color.BLACK, Shader.TileMode.CLAMP));
        // 绘制一个跟图片大小一样的矩形
        canvas.drawRect(x, y, x + mBitmap.getWidth(), y + mBitmap.getHeight(), mShaderPaint);

姿势二存在的问题:

  • 只能是圆形的渐变,对颜色区域的把控能力不足 ;

姿势2.1 矩阵亮相

MatrixPs.png
    // 根据我们源图的大小生成暗角Bitmap
        darkCornerBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        // 将该暗角Bitmap注入Canvas
        Canvas canvas = new Canvas(darkCornerBitmap);
        // 计算径向渐变半径
        float radiu = canvas.getHeight() * (2F / 3F);
        // 实例化Shader图形的画笔
        mShaderPaint = new Paint();
        // 设置径向渐变,渐变中心当然是图片的中心也是屏幕中心,渐变半径我们直接拿图片的高度但是要稍微小一点
        // 中心颜色为透明而边缘颜色为黑色
        RadialGradient radialGradient = new RadialGradient(screenW / 2 , screenH / 2 ,
                mBitmap.getHeight() * 7 / 8, Color.TRANSPARENT, Color.BLACK, Shader.TileMode.CLAMP);
        // 实例化一个矩阵
        Matrix matrix = new Matrix();
        // 设置矩阵的缩放
        matrix.setScale(canvas.getWidth() / (radiu * 2F), 1.0F);
        // 设置矩阵的预平移
        matrix.preTranslate(((radiu * 2F) - canvas.getWidth()) / 2F, 0);
        // 将该矩阵注入径向渐变
        radialGradient.setLocalMatrix(matrix);
        mShaderPaint.setShader(radialGradient);
        // 绘制矩形
        canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mShaderPaint);
               把姿势二onDraw换成
        //绘制我们画好的径向渐变图      
        canvas.drawBitmap(darkCornerBitmap, x, y, null);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容