美文网首页
自定义控件其实很简单(笔记一)

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

作者: 北疆_ | 来源:发表于2016-04-15 10:27 被阅读203次

    目录

    • [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);
    

    相关文章

      网友评论

          本文标题: 自定义控件其实很简单(笔记一)

          本文链接:https://www.haomeiwen.com/subject/xtjbhttx.html