CircleImageView的工作原理

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,最后再利用这个画笔绘制一个圆形即可画出我们设置的图片,而且是圆形,所以利用这个原理,我们可以画任何我们想要的形状哦。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • Android基础及相关机制 Android Context 上下文 你必须知道的一切 Android中子线程真的...
    楷桐阅读 1,998评论 1 30
  • Draw Canvas 可以绘制的对象:弧线(arcs)、填充颜色(argb和color)、圆(circle和ov...
    JeremyDai阅读 533评论 0 0
  • 正值冬天,好多日受到雾霾侵袭,甚是忧郁,幸好一场冬雨,使阳光再现,也给我们触摸冬天的机会。 于是到公园走走,停停,...
    小苜蓿阅读 505评论 4 11
  • 今晚还是继续来找失眠的原因。除了跑步,戒游戏,还有就是看书,大概也没其他的改变了吧。 跑步说了,游戏说了,那么现在...
    噢天气不错阅读 80评论 0 0
  • 我的心里有座城,城中有一个荷叶湖,湖中心有一个专属于我自己的小筑,围绕着湖的周边有四个酒馆, 一曰:《逢燕楼》,与...
    不争不抢不忧阅读 255评论 0 6