首先这篇文章我们从以下四个方面进行一一讲解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文件与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了,所以不用去拷贝如下代码:
如果您没有配置过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
好了,到现在我们本篇文章就该结束了,希望对您有所帮助,谢谢,如果哪里写的不对,希望留言指正!