首先这篇文章我们从以下四个方面进行一一讲解1.CircleImageView在AS中集成及用法;2.CircleImageView中定义的对外的方法;3.源码解析;4.用到的知识点的总计。希望通过本篇文章的学习,您会对自定义控件及自定义控件中用到的一些类有一定的了解,最后我会把我添加好详细注释的Demo下载地址附上,你也可以一边看我的Demo,一边看这篇文章,效果应该会更好,那么,我们这就开始吧!

1.CircleImageView在AS中集成及用法

我们想在AS下使用CircleImageView只需要在我们app下的build.gradle中添加一行配置即可,很简单,如下最后一行:

dependencies {compile fileTree(include: ['*.jar'], dir: 'libs')compile 'com.android.support:appcompat-v7:25.0.0'compile 'de.hdodenhof:circleimageview:2.1.0'
}

配置完了以后,我们就可以在我们的xml布局文件中引入或者.java文件中创建我们的CircleImageView并且使用了,到目前为止它的最新版本为2.1.0,它在github上的下载地址如下:https://github.com/hdodenhof/CircleImageView 好吧,集成就讲这么点,很简单。

下面我们讲一下CircleImageView的具体用法,在将具体用法之前,我们先新建一个最新的android项目按照上述集成方法,集成我们的CircleImageView,集成完成后我们就可以在我们的XML布局文件中引入我们的控件了,如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"><de.hdodenhof.circleimageview.CircleImageViewandroid:id="@+id/circleImageView"android:layout_width="150dp"android:layout_height="150dp"android:src="@mipmap/psb"app:civ_border_color="@android:color/holo_red_dark"app:civ_border_width="5dp" />
</RelativeLayout>

然后将XML文件与Acitivity进行绑定,运行项目就会看到如下结果:

我们还可以在代码中对CircleImageView进行一些设置如禁用图片圆形属性等,这些在下一节CircleImageView中定义的对外的方法中讲解。

2.CircleImageView中定义的对外的方法

我们打开CircleImageView.java文件查看他的public方法,下边我一一说明:

@Override
public ScaleType getScaleType() {return SCALE_TYPE;
}

解释:外部类获取CircleImageView的ScaleType属性。

@Override
public void setScaleType(ScaleType scaleType) {if (scaleType != SCALE_TYPE) {throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));}
}

解释:重写父类方法,,给CircleImageView设置ScaleType属性,这里需要注意如果设置的scaleType不是ScaleType.CENTER_CROP,抛出异常,只支持ScaleType.CENTER_CROP;

@Override
public void setAdjustViewBounds(boolean adjustViewBounds) {if (adjustViewBounds) {throw new IllegalArgumentException("adjustViewBounds not supported.");}
}

解释:重写父类方法,adjustViewBounds属性为是否保持宽高比。需要与maxWidth、MaxHeight一起使用,否则单独使用没有效果。当前控件不支持设置保持宽高比;起到禁止设置的作用。

@Override
public void setPadding(int left, int top, int right, int bottom) {super.setPadding(left, top, right, bottom);setup();
}

解释:设置padding属性,该控件兼容设置padding属性。

@Override
public void setPaddingRelative(int start, int top, int end, int bottom) {super.setPaddingRelative(start, top, end, bottom);setup();
}

解释:setPadding的话不管方向如何都按照左上右下的顺序来配置Padding,setPaddingRelative的话则会按照配置的LayoutDirection来进行设置,从左到右的话为左上右下,从右到左的话为右上左下的顺序(Android4.0以后添加)。

public int getBorderColor() {return mBorderColor;
}

解释:获取外边框圆环颜色。

public void setBorderColor(@ColorInt int borderColor) {if (borderColor == mBorderColor) {return;}mBorderColor = borderColor;mBorderPaint.setColor(mBorderColor);invalidate();
}

解释:设置外边框圆环颜色。

public int getBorderWidth() {return mBorderWidth;
}

解释:获取外边框宽度。

public void setBorderWidth(int borderWidth) {if (borderWidth == mBorderWidth) {return;}mBorderWidth = borderWidth;setup();
}

解释:设置外边框宽度。

public boolean isBorderOverlay() {return mBorderOverlay;
}

解释:外边圆环是否压住圆形图片。

public void setBorderOverlay(boolean borderOverlay) {if (borderOverlay == mBorderOverlay) {return;}mBorderOverlay = borderOverlay;setup();
}

解释:设置外边圆环是否压住内部圆形图片。

public boolean isDisableCircularTransformation() {return mDisableCircularTransformation;
}

解释:是否禁用图片圆形属性。如果为true,则就是普通方形图片。

public void setDisableCircularTransformation(boolean disableCircularTransformation) {if (mDisableCircularTransformation == disableCircularTransformation) {return;}mDisableCircularTransformation = disableCircularTransformation;initializeBitmap();
}

解释:设置是否禁用图片圆形属性。

@Override
public void setImageBitmap(Bitmap bm) {super.setImageBitmap(bm);initializeBitmap();
}
@Override
public void setImageDrawable(Drawable drawable) {super.setImageDrawable(drawable);System.out.println("Log_setImageDrawable()");initializeBitmap();
}
@Override
public void setImageResource(@DrawableRes int resId) {super.setImageResource(resId);System.out.println("Log_setImageResource()");initializeBitmap();
}
@Override
public void setImageURI(Uri uri) {super.setImageURI(uri);initializeBitmap();
}

解释:四种重写父类设置图片方法。

PS:如果我们在XML中设置了android:src属性,会执行我们的第一个方法(setImageBitmap)该方法会先于构造函数调用之前调用。后边在源码讲解中详细说明。

@Override
public void setColorFilter(ColorFilter cf) {if (cf == mColorFilter) {return;}mColorFilter = cf;applyColorFilter();invalidate();
}

解释:重写父类方法,设置ColorFilter,查看ColorFilter文档你会发现,ColorFilter有三个子类:ColorMatrixColorFilter:颜色矩阵过滤器;LightingColorFilter:“光照色彩过滤器”,模拟一个光照照过图像所产生的效果;PorterDuffColorFilter:PorterDuff混合模式的色彩过滤器。如果你想了解相关知识可以查相关文档,这里就不详细讲了,超出本文范围。

@Override
public ColorFilter getColorFilter() {return mColorFilter;
}

解释:获取着色器。

到目前位置,整个CircleImageView中的建议使用的公共方法差不多就上述这么些,还有一些现在已经不建议使用了,我就没有拿出来,比如设置图片背景颜色啊等等,已经用注解@Deprecated进行了标注,如下:

@Deprecated
public void setFillColor(@ColorInt int fillColor) {if (fillColor == mFillColor) {return;}System.out.println("Log_setFillColor()");mFillColor = fillColor;mFillPaint.setColor(fillColor);invalidate();
}

我们CircleImageView的所有public方法都进行了说明,那我们的控件您肯定就会用了,再不会用,我相信你已经没救了,赶紧骑上大母猪飞奔吧!

3.源码解析

这个小结我们开始进入本篇的重点,就是了解它是如何实现的,在我们讲解以前,为了添加注释方便,我们先在我们的项目下新建一个名称一模一样的.java文件,将原CircleImageView文件中代码复制一份,粘进去,可以看到我们的控件是继承了ImageView的,在ImageView的基础上进行扩展。这样就可以了。因为我们之前已经在app下的build.gradle中引入过CircleImageView了,所以不用去拷贝如下代码:

<resources><declare-styleable name="CircleImageView"><attr name="civ_border_width" format="dimension" /><attr name="civ_border_color" format="color" /><attr name="civ_border_overlay" format="boolean" /><attr name="civ_fill_color" format="color" /></declare-styleable>
</resources>

如果您没有配置过build.gradle,就需要复制了,否则会报错。下面我们将XML中的引用改成我们自己刚建的CircleImageview运行,结果依然可以显示,没有任何区别。好了下面我们进入主题吧!

打开我们的CircleimageView你会发现,它也有三个构造函数,如下:

public CircleImageView(Context context) {super(context);System.out.println("Log_单参构造");init();
}public CircleImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public CircleImageView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);System.out.println("Log_多参构造");TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH);mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR);mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY);mFillColor = a.getColor(R.styleable.CircleImageView_civ_fill_color, DEFAULT_FILL_COLOR);a.recycle();init();
}

第一个构造函数是在代码中new对象的时候执行,第二个是在XML中引用的时候调用,这里跟我们一般定义控件没什么区别。我们接下来就寻找程序入口,看他是如何运行的,按照我们一般使用View来说,首先看一种情况,在XML里边引用,并且不设置android:src属性,我们知道,在XML里边引用程序会走我们的第二个构造函数,好吧,我们看一下第二个构造函数:

public CircleImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);
}

可以看到,在我们的第二个构造函数中调用了我们第三个构造函数,三参构造函数如下:

public CircleImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);
}
可以看到,在我们的第二个构造函数中调用了我们第三个构造函数,三参构造函数如下:
public CircleImageView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);System.out.println("Log_多参构造");TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH);mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR);mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY);mFillColor = a.getColor(R.styleable.CircleImageView_civ_fill_color, DEFAULT_FILL_COLOR);a.recycle();init();
}

这里很简单,就是通过TypedArray获取我们在XML中设置的参数值并赋值给相应参数,外圆环宽度、外圆环颜色、圆环是否压住图片、图片背景。然后调用了init()方法,下边看一下init()方法:

private void init() {super.setScaleType(SCALE_TYPE);mReady = true;if (mSetupPending) {setup();mSetupPending = false;}
}

可以看见在这里调用了父类的setScaleType()方法传入了一个SCALE_TYPE变量,这是什么东西呢?看一下它的定义,

private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;

可以看到它是final的,是不可以修改的,在本篇的第二小结设置ScaleType()属性时,也可以看到,我们继承ImageView后的CircleImageView只支持CENTER_CROP这一种设置,

关于ImageView.ScaleType()相关知识可以查看如下文章:

http://blog.csdn.net/buaaroid/article/details/49360779 接着它将mRead置为true,这里想不用管,只知道她在初始化的时候是false就行了。接着往下,判断了一个mSetupPending属性,这个属性因为在一开是false的,所以不会进入括号内,所以更不会调用我们的setup()方法。难道这样就完了吗?不会,因为我们在XML中没有设置图片相关信息,那么我们肯定要在代码里边设置了,那么我们在代码里就需要绑定xml中的View然后调用circleImageView.setImageResource(R.mipmap.psb);我们在上一节中说过在CircleImageView中用四个方法可以设置图片,这个就是其中之一,好吧,我们接着看它的内部实现:

@Override
public void setImageResource(@DrawableRes int resId) {super.setImageResource(resId);initializeBitmap();
}

在代码中调用父类的setImageResource()方法设置图片,并调用initializeBitmap()方法,继续看:

private void initializeBitmap() {if (mDisableCircularTransformation) {mBitmap = null;} else {mBitmap = getBitmapFromDrawable(getDrawable());}setup();
}

判断是否禁止圆形属性,禁止mBitmap为null,不禁止获取到我们设置的Drawable并通过getBitmapFromDrawable()方法转换成mBitmap,然后调用setup()方法,看setup()方法:

private void setup() {if (!mReady) {mSetupPending = true;return;}if (getWidth() == 0 && getHeight() == 0) {return;}if (mBitmap == null) {invalidate();return;}
//        TileMode:(一共有三种)
//        CLAMP  :如果渲染器超出原始边界范围,会复制范围内边缘染色。
//        REPEAT :横向和纵向的重复渲染器图片,平铺。
//        MIRROR :横向和纵向的重复渲染器图片,这个和REPEAT重复方式不一样,他是以镜像方式平铺。mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);//抗锯齿mBitmapPaint.setAntiAlias(true);mBitmapPaint.setShader(mBitmapShader);//Paint.Style.FILL:填充内部//Paint.Style.FILL_AND_STROKE  :填充内部和描边//Paint.Style.STROKE  :描边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);//取的原图片的宽高mBitmapHeight = mBitmap.getHeight();mBitmapWidth = mBitmap.getWidth();mBorderRect.set(calculateBounds());//计算整个圆形带Border部分的最小半径,取mBorderRect的宽高减去一个边缘大小的一半的较小值mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);//初始图片显示区域为mBorderRect(CircleImageView中图片区域的实际大小)mDrawableRect.set(mBorderRect);if (!mBorderOverlay && mBorderWidth > 0) {//到现在图片区域Rect(mDrawableRect)与整个View所用Rect(mBorderRadius)相同【mDrawableRect.set(mBorderRect)设置】,//如果在xml中设置app:civ_border_overlay="false"(边框不覆盖图片)并且外框宽度大于0,将图片显示区域Rect向内(缩小)mBorderWidth-1.0f。// inset()方法参数为正数表示缩小,为复数表示扩大区域。mDrawableRect.inset(mBorderWidth - 1.0f, mBorderWidth - 1.0f);}//计算内圆最小半径,即去除边框后的Rect(内部图片Rect->mDrawableRect)宽度的半径mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);applyColorFilter();updateShaderMatrix();invalidate();}

看一下,很长,没错,这个方法是当前类里最主要的一个方法,先说一下它做了几件事:

1.上来三个判断,一会说。

2.设置三个重要的paint及mBitmapPaint(画内部圆形图片用到的Paint)、mBorderpaint(画外部圆环用到的paint)、mFillPaint(画图片背景用到的paint)。

3.设置mBorderRect(外部圆环所占矩形区域)、mBorderRadius(外部圆环半径)、mDrawableRect(内部图片所占矩形区域)、mDrawableRaduis(内部圆形图片半径)。

4.设置颜色过滤器。

5.设置BitmapShader的Matrix,设置缩放比,平移。

6.调用invaladate()刷新界面。

这就是在setup()方法中干的几件事情,下面我们详细说明,回到代码,首先是三个判断,第一个判断mReady,因为我们构造函数中已经将其变成了true,所以不会进入内部,而是继续向下走。这里进一段小插曲,到目前为止,肯定很多人不明白,这个mReady及内部的mSetupPending 是干什么用的,这里说明一下,回到前边说的在XML中引入,但是没有设置android:src属性,以上都是它的执行顺序,那么,我们换另一种方式,及在XML文件中加入android:src属性,运行代码,你会发现,我们四个设置图片方法的第一个方法(setImageBitmap()方法)会被执行,而且是在构造方法以前执行,我们知道,在它里边也间接的调用了我们的setup()方法,但是此时我们的构造函数还没有执行,也就是说一些参数还没有被初始化,所以现在肯定是不能进行后续操作的,所以在这种情况下,当执行到setup()方法的时候第一个mReady(初始化为false)判断是过不去的,只是把mSetupPending设置成了true,然后return。接着才会执行我们的构造函数,在构造函数里边同样有一个关于mReady与mSetuppending的操作,在init()中,

private void init() {super.setScaleType(SCALE_TYPE);mReady = true;if (mSetupPending) {setup();mSetupPending = false;}
}

因为我们前边在setImageBitmap()中将mSetupPending设置为了true,所以会进入setup()方法,说了这么多,不知道你听懂了没,多想多看几遍,相信你肯定能明白设计mReady、与mSetupPending的意义,就是在不同的情况下保证程序以正确的方式进行逻辑处理。多看几遍,只能帮你到这了。

插曲还挺长,接着看我们setup()中的代码,后续两个判断一个当前View宽高为0退出,一个没有获取到mBitmap退出,没什么好说的。接着往下走:

TileMode:(一共有三种)
//        CLAMP  :如果渲染器超出原始边界范围,会复制范围内边缘染色。
//        REPEAT :横向和纵向的重复渲染器图片,平铺。
//        MIRROR :横向和纵向的重复渲染器图片,这个和REPEAT重复方式不一样,他是以镜像方式平铺。mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//抗锯齿
mBitmapPaint.setAntiAlias(true);mBitmapPaint.setShader(mBitmapShader);//Paint.Style.FILL:填充内部
//Paint.Style.FILL_AND_STROKE  :填充内部和描边
//Paint.Style.STROKE  :描边
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);

相信一些设置画笔的没什么好说的吧。看一下设置矩形跟半径相关的吧,如下:

//取的原图片的宽高
mBitmapHeight = mBitmap.getHeight();
mBitmapWidth = mBitmap.getWidth();
mBorderRect.set(calculateBounds());
//计算整个圆形带Border部分的最小半径,取mBorderRect的宽高减去一个边缘大小的一半的较小值
mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);
//初始图片显示区域为mBorderRect(CircleImageView中图片区域的实际大小)
mDrawableRect.set(mBorderRect);
if (!mBorderOverlay && mBorderWidth > 0) {//到现在图片区域Rect(mDrawableRect)与整个View所用Rect(mBorderRadius)相同【mDrawableRect.set(mBorderRect)设置】,//如果在xml中设置app:civ_border_overlay="false"(边框不覆盖图片)并且外框宽度大于0,将图片显示区域Rect向内(缩小)mBorderWidth-1.0f。// inset()方法参数为正数表示缩小,为复数表示扩大区域。mDrawableRect.inset(mBorderWidth - 1.0f, mBorderWidth - 1.0f);
}
//计算内圆最小半径,即去除边框后的Rect(内部图片Rect->mDrawableRect)宽度的半径
mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);

可以看到在设置外环矩形时调用了一个calculateBounds()方法看看里边的实现:

private RectF calculateBounds() {//获取当前CircleImageView视图除去PaddingLeft与PaddingRight后剩余的可用宽度// (如果你设置的PaddingLeft+PaddingRight>+当前控件的宽度,当前控件会显示不出来);int availableWidth = getWidth() - getPaddingLeft() - getPaddingRight();//获取当前CircleImageView视图除去PaddingTop与PaddingBottom后剩余的可用高度;int availableHeight = getHeight() - getPaddingTop() - getPaddingBottom();//获取除去Padding后宽高剩余可用空间较小的一个值。int sideLength = Math.min(availableWidth, availableHeight);//如果最后得到的availableWidth与availableHeight不一样(我们在代码中设置的原因),大的要向小的靠齐,// 最终得到的RectF为正方形。float left = getPaddingLeft() + (availableWidth - sideLength) / 2f;float top = getPaddingTop() + (availableHeight - sideLength) / 2f;return new RectF(left, top, left + sideLength, top + sideLength);
}

代码我已经加好了注释,多看几遍。设置好圆环矩形后,计算整个圆形带Border部分的最小半径,注意这里计算半径时宽高需要减去mBorderWidth再除以2,取mBorderRect的宽高减去一个边缘大小的一半的较小值做为半径。然后将mBorderRect设置给mDrawableRect,然后判断我们是否设置了圆环压住圆形图片并且mBorderWidth>0,如下:

if (!mBorderOverlay && mBorderWidth > 0) {mDrawableRect.inset(mBorderWidth - 1.0f, mBorderWidth - 1.0f);
}

如果都满足,则mDrawableRect的x,y都缩小(mBorderWidth-1.0f),然后计算出mDrawableRaduis。

然后调用了applyColorFilter()方法,看一下:

private void applyColorFilter() {if (mBitmapPaint != null) {mBitmapPaint.setColorFilter(mColorFilter);}
}

可以看到,就是给mBitmapPaint设置了mColorFiter,mColorFiter是通过上一节中的public方法设置的,如果我们没有设置,mColorFilter为null。

然后是我们的updateShaderMatrix(),这个方法也很重要,看一下:

private void updateShaderMatrix() {float scale;float dx = 0;float dy = 0;mShaderMatrix.set(null);//比较图片和所绘区域宽缩放比、高缩放比,那个小。取小的,作为矩阵的缩放比。//代码不太好理解,等价于(mBitmapWidth / mDrawableRect.width()) > (mBitmapHeight / mDrawableRect.height())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);//平移操作,(dx + 0.5f)的处理,是四舍五入mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);mBitmapShader.setLocalMatrix(mShaderMatrix);
}

东西不多,但是重要啊,在计算缩放比scale时有一行代码不好理解,也转换了一下,好理解一点了,这里可以自己画一个图理解一下,下面是我举得一个例子,你可以对着我画的图,理解一下:

mShaderMatrix按照算出来的scale进行缩放,并进行相应的平移,最后赋给mBitmapShader,mBitmapShader在setup()方法中已经付给了mBitmapPaint。最后就是调用invaladate()刷新界面了,调用invaladate()会执行onDraw()方法,下边看一下:

@Override
protected void onDraw(Canvas canvas) {// 是否允许转换成圆形设置if (mDisableCircularTransformation) {super.onDraw(canvas);return;}if (mBitmap == null) {return;}//如果设置了图片底色,绘制图片底色。if (mFillColor != Color.TRANSPARENT) {canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mFillPaint);}//画内部图片区域(我们给mBitmapPaint设置了Shader,给Shader设置了LocalMatrix,通过ShaderMatrix设置了缩放比,及平移操作完成功能);canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mBitmapPaint);//如果设置了BorderWidth宽度,绘制;if (mBorderWidth > 0) {canvas.drawCircle(mBorderRect.centerX(), mBorderRect.centerY(), mBorderRadius, mBorderPaint);}
}

OnDraw()方法很简单,就是用我们在setup()方法中设置的画笔进行绘画。在我们的上一节中的一些共有方法中会看到,其实很多方法都调用了invaladate()或者是setup()方法,对view进行了重新绘制。

到此,我们的代码就讲解完了,不知道你对CircleImageView的实现更加了解了没,如果看一遍看不明白,多看几遍。

4.用到的知识点的总计

通过源码分析我们可以知道,代码中作者用到了下边一些东西辅助完成功能:

1.ImageView.ScaleType

2.RectF

3.Matrix

4.Paint

5.BitmapShader

6.ColorFilter

我已将我加好详细注释的整个Demo文档上传至CSDN,你可以在下边连接进行下载:

http://download.csdn.net/detail/liuyonglei1314/9754395

好了,到现在我们本篇文章就该结束了,希望对您有所帮助,谢谢,如果哪里写的不对,希望留言指正!

CircleImageView用法及源码解析(雷惊风)相关推荐

  1. DeepLink用法及源码解析

    终于建了一个自己个人小站:https://huangtianyu.gitee.io,以后优先更新小站博客,欢迎进站,O(∩_∩)O~~ 1. 简介 DeepLink官网上有这样的解释: When a ...

  2. MultiDex 不得不说的用法与源码解析

    前言   开发Android应用或者SDK方向小伙伴们,经过多版本的迭代,新功能的不断增加,依赖多个开源项目,使用第三方SDK,都会导致Apk大小急速膨胀.最终会导致方法超限这一问题,下面让大家了解下 ...

  3. require用法及源码解析

    一.require()的基本用法 require语句内部逻辑: 当 Node 遇到 require(X) 时,按下面的顺序处理. (1)如果 X 是内置模块(比如 require('http'))  ...

  4. 什么是LinkedList?什么时候使用它呢?Java LinkedList结构、用法及源码解析

    前言:我们学习java时都知道ArrayList实现List接口,LinkedList也实现List接口,但我们平时用的时候LinkedList却很少被用到.那么,LinkedList什么时候该用到呢 ...

  5. Handler全家桶之 —— Handler 源码解析

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 本文首发于本人简书 前言 好记性不如烂笔头. 这是一个系列文章,将会包括: Handler全家桶之 -- Handler 源码解析 ...

  6. php 框架源码分析,Laravel框架源码解析之模型Model原理与用法解析

    本文实例讲述了Laravel框架源码解析之模型Model原理与用法.分享给大家供大家参考,具体如下: 前言 提前预祝猿人们国庆快乐,吃好.喝好.玩好,我会在电视上看着你们. 根据单一责任开发原则来讲, ...

  7. PrepareStatement用法(附源码解析)

    PrepareStatement 基本用法 1. 加载驱动 首先在pom.xml 中引入 mysql 依赖 <dependency><groupId>mysql</gro ...

  8. Spring源码解析【完整版】--【bilibili地址:https://www.bilibili.com/video/BV1oW41167AV】

    [本文为bilibili视频雷丰阳的Spring源码解析的完整版总结文章,其中文章前面大部分为他人博文的搬运,后面补充了其未总结的部分] 一.Java的注解 1. 注解的概念 注释:用文字描述程序,给 ...

  9. Volley 源码解析之图片请求

    一.前言 上篇文章我们分析了网络请求,这篇文章分析对图片的处理操作,如果没看上一篇,可以先看上一篇文章Volley 源码解析之网络请求.Volley 不仅仅对请求网络数据作了良好的封装,还封装了对图片 ...

  10. dataset__getitem___PyTorch源码解析与实践(1):数据加载Dataset,Sampler与DataLoader

    献给学习PyTorch在路上或者计划较深入理解PyTorch的同行者们 写在前面 笔者一直使用tf,大势所趋决定转PyTorch,这个系列就作为我学习PyTorch的笔记与心得. 网络上PyTorch ...

最新文章

  1. Linux用户root登录shell时,linux系统管理-定制root用户的Shell环境
  2. PHP正则表达式规则及常用方法整理
  3. 比尔盖茨跌落第二!世界首富换人了
  4. SpringBoot使用JdbcTemplate案例(学习笔记)
  5. cd rw 多少次_程序员:想知道你每天按了多少次键盘吗?
  6. JavaScript知识概要
  7. uc浏览器TV版最新版本功能简介
  8. 【OpenCV 例程200篇】22. 图像添加非中文文字
  9. react使用less预编译语言和本地代理配置
  10. iis解析错误的编号_2019年网络工程师考试试题及解析(上午21-44题)
  11. 阻止具有特定文件扩展名的附件的电子邮件
  12. 《CCNA学习指南:数据中心(640-911)》——1.2 一般网络的构成
  13. HMDD:miRNA相关疾病数据库
  14. python实现鼠标键盘事件_鼠标与键盘操作事件
  15. 反转链表的Java实现
  16. Matlab批量修改文件格式
  17. 关于iphone5和iphone4兼容的尺寸问题
  18. 机房ip和住宅ip的区别是什么?
  19. you-get下载bilibili视频
  20. OpenAI生成二次元美女【辣眼睛慎入】

热门文章

  1. AutoSAR入门到精通讲解 (AuroSAR-CP描述) 1.1 AutoSAR-CP简介
  2. 如何生成java dump文件
  3. Java课程寒假之开发记账本软件(网页版)之三
  4. python实现xlsx批量转xls(或者xls批量转xlsx)
  5. jquery的siblings属性和定时器
  6. PTB中的Screen函数
  7. android电话通讯录导入iphone6,怎么把小米手机通讯录导入iphone6?
  8. PPO:Proximal Policy Optimization Algorithms
  9. 以下关于c语言程序中函数的说法正确的是( ),以下关于C语言程序中函数的说法正确的是:(  )...
  10. 服务器开机系统进不去怎么办,开机就进BIOS进不去系统怎么处理