CircleImageView的工作原理

作者: 大飛機 | 来源:发表于2017-03-30 13:57 被阅读322次

    CircleImageView的工作原理

    项目地址:CircleImageView

    在分析CircleImageView源码之前,先学习一些知识点

    知识点1:BitmapShader

    BitmapShader 继承自Shader,而Shader有5个子类:

    • BitmapShader
    • ComposeShader
    • LinearGradient
    • RadialGradient
    • SweepGradient

    首先来认识一下Shader是什么?

    直接翻译过来叫着色器,没了解过的人看了会感觉很蒙,就跟把Socket翻译成套接字似的,现实生活在跟本没有这玩意啊,怎么理解呢?

    经过我细致的揣摩,我认为Shader就像是一把刷子或者说是印章,它决定写出来的东西是什么颜色的,或者画出到图案是什么样子到。

    比如说我想要制作一台车,在所有都组装完毕之后,加了油就可以开了,但是看起来怎么这么不狂拽炫吊炸天呢? 哦对了,忘了给车喷漆了,这时候我们就拿几喷漆罐对着车喷一些文字或图案,那喷图案的过程就是Shader的过程.

    我们上面提到有5种Shader:

    • BitmapShader ------ 图案喷漆
    • ComposeShader ------ 将其他喷漆合并
    • LinearGradient ------ 线性渐变色的喷漆
    • RadialGradient ------ 环形渐变色的喷漆
    • SweepGradient ------ 扫描喷漆(类似于从某一个点射出很多不同颜色的射线)

    那么我们重点关注一下BitmapShader,它是一个可以将图案绘制出来的喷漆,比如说我要把小狗的图像作为一个喷漆,那么我拿着这个喷漆对着车喷一下,车马上就会有一只可爱的小狗的图画.

    知识点2:ColorFilter

    这个通过直译就比较好理解,颜色过滤器,就像是一个筛子,比如说我有3种类型的物体:三角形,矩形,圆形,让他们通过管道从一个地方传递到另外一个地方,如果管道是完全通透的,那么,这些物体从起始点到终点都是一样的,那么如果我中间加一个三角形过滤器,只有三角形能通过,那么剩下矩形和圆形就过不去了,颜色过滤器类似,但是它能设置量,既通过的多少.

    ColorFilter在Android中是一个5* 4的矩阵:

            {
                1, 0, 0, 0, 0    // 颜色种的红色R
                0, 1, 0, 0, 0    // 颜色种的绿色G
                0, 0, 1, 0, 0    // 颜色种的蓝色B
                0, 0, 0, 1, 0    // 颜色种的透明度A
            }
    

    如上所示,其中1的位置就是每个颜色的过滤器,范围0 ~ 2,其中1表示正常颜色,0表示完全隔断,2表示FF,所以我们如果要调整一张图片的颜色,只要调整ColorFilter种的值就可以做到了.

    知识点3:Matrix

    这里讨论的是Android中android.graphics.Matrix这个类

    Android种的Martix是用来做图像调整的,如平移,缩放,旋转,还有错切,它是一个3 * 3的矩阵.

    {
    缩放X, 错切X, 平移X,
    错切Y, 缩放Y,  平移Y,
    透视1, 透视2,  透视3,
    }
    

    其中透视基本不用,咱主要用到的是平移和缩放。


    正式开始

    public class CircleImageView extends ImageView {
        //首先强制设置缩放类型为CENTER_CROP
        private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
        
        //其次,Bitmap类型为ARGB_8888
        private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
        private static final int COLORDRAWABLE_DIMENSION = 2;
    
        private static final int DEFAULT_BORDER_WIDTH = 0;
        private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
        private static final int DEFAULT_FILL_COLOR = Color.TRANSPARENT;
        private static final boolean DEFAULT_BORDER_OVERLAY = false;
    

    然后在UI在创建时会执行init();

        private void init() {
            //设置ScaleType
            super.setScaleType(SCALE_TYPE);
            mReady = true;
    
            if (mSetupPending) {
                //重点看一下setup
                setup();
                mSetupPending = false;
            }
        }
    

    我们重点关注一下setup()

        private void setup() {
            ...
    
            //初始化一个BitmapShader,就是上面讲的会喷图的喷漆
            mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
            //将喷图赋予mBitmapPaint这个画笔,感觉上面的比喻有点不对,BitmapShader应该只是一个图案,喷漆应该是Paint
            mBitmapPaint.setAntiAlias(true);
            mBitmapPaint.setShader(mBitmapShader);
    
            //圆环形的边框用的画笔
            mBorderPaint.setStyle(Paint.Style.STROKE);
            mBorderPaint.setAntiAlias(true);
            mBorderPaint.setColor(mBorderColor);
            mBorderPaint.setStrokeWidth(mBorderWidth);
    
            //填充用到的画笔
            mFillPaint.setStyle(Paint.Style.FILL);
            mFillPaint.setAntiAlias(true);
            mFillPaint.setColor(mFillColor);
    
            //取到bitmap的宽高信息
            mBitmapHeight = mBitmap.getHeight();
            mBitmapWidth = mBitmap.getWidth();
    
            //计算圆形头像的范围,如果宽 > 高,则用高作为正方形边长
            mBorderRect.set(calculateBounds());
            //半径为宽或者高的一半
            mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);
            //DrawableRect为里面图案的范围,而BorderRect则是边框的范围
            mDrawableRect.set(mBorderRect);
            if (!mBorderOverlay && mBorderWidth > 0) {
                //缩小mBorderWidth - 1 个像素
                mDrawableRect.inset(mBorderWidth - 1.0f, mBorderWidth - 1.0f);
            }
            mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);
    
            //设置颜色过滤器
            applyColorFilter();
            //更新Matrix
            updateShaderMatrix();
            //刷新UI
            invalidate();
        }
    

    接下来再看看updateShaderMatrix

        private void updateShaderMatrix() {
            float scale;
            float dx = 0;
            float dy = 0;
            //reset the matrix
            mShaderMatrix.set(null);
            //计算从原始的bitmap到UI需要显示的bitmap,需要的缩放和位移,其中,如果原图宽度大于高度,则只位移X,否则位移Y
            if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
                scale = mDrawableRect.height() / (float) mBitmapHeight;
                dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
            } else {
                scale = mDrawableRect.width() / (float) mBitmapWidth;
                dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
            }
    
            //传入缩放设置
            mShaderMatrix.setScale(scale, scale);
            //传入位移设置
            mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);
            //将Martix设置给BitmapShader
            mBitmapShader.setLocalMatrix(mShaderMatrix);
        }
    

    所以,通过查看源码我们就能了解到,CircleImageView的工作原理是将ImageView的Bitmap捕获到,然后将这个Bitmap设置给BitmapShader,其中利用Matrix调整Bitmap在BitmapShader中的大小为ImageView大小并且位置居中,然后把BitmapShader利用Paint.setShader赋值给画笔Paint,最后再利用这个画笔绘制一个圆形即可画出我们设置的图片,而且是圆形,所以利用这个原理,我们可以画任何我们想要的形状哦。

    相关文章

      网友评论

        本文标题:CircleImageView的工作原理

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