




已阅读5页,还剩14页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
CircleImageView用法及源码解析首先这篇文章我们从以下四个方面进行一一讲解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上的下载地址如下:/hdodenhof/CircleImageView 好吧,集成就讲这么点,很简单。下面我们讲一下CircleImageView的具体用法,在将具体用法之前,我们先新建一个最新的android项目按照上述集成方法,集成我们的CircleImageView,集成完成后我们就可以在我们的XML布局文件中引入我们的控件了,如下: 然后将XML文件与Acitivity进行绑定,运行项目就会看到如下结果:我们还可以在代码中对CircleImageView进行一些设置如禁用图片圆形属性等,这些在下一节CircleImageView中定义的对外的方法中讲解。2. CircleImageView中定义的对外的方法我们打开CircleImageView.java文件查看他的public方法,下边我一一说明:Overridepublic ScaleType getScaleType() return SCALE_TYPE;解释:外部类获取CircleImageView的ScaleType属性。Overridepublic 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;Overridepublic void setAdjustViewBounds(boolean adjustViewBounds) if (adjustViewBounds) throw new IllegalArgumentException(adjustViewBounds not supported.); 解释:重写父类方法,adjustViewBounds属性为是否保持宽高比。需要与maxWidth、MaxHeight一起使用,否则单独使用没有效果。当前控件不支持设置保持宽高比;起到禁止设置的作用。Overridepublic void setPadding(int left, int top, int right, int bottom) super.setPadding(left, top, right, bottom); setup();解释:设置padding属性,该控件兼容设置padding属性。Overridepublic 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();解释:设置是否禁用图片圆形属性。Overridepublic void setImageBitmap(Bitmap bm) super.setImageBitmap(bm); initializeBitmap();Overridepublic void setImageDrawable(Drawable drawable) super.setImageDrawable(drawable); System.out.println(Log_setImageDrawable(); initializeBitmap();Overridepublic void setImageResource(DrawableRes int resId) super.setImageResource(resId); System.out.println(Log_setImageResource(); initializeBitmap();Overridepublic void setImageURI(Uri uri) super.setImageURI(uri); initializeBitmap();解释:四种重写父类设置图片方法。PS:如果我们在XML中设置了android:src属性,会执行我们的第一个方法(setImageBitmap)该方法会先于构造函数调用之前调用。后边在源码讲解中详细说明。Overridepublic void setColorFilter(ColorFilter cf) if (cf = mColorFilter) return; mColorFilter = cf; applyColorFilter(); invalidate();解释:重写父类方法,设置ColorFilter,查看ColorFilter文档你会发现,ColorFilter有三个子类:ColorMatrixColorFilter:颜色矩阵过滤器;LightingColorFilter:“光照色彩过滤器”,模拟一个光照照过图像所产生的效果;PorterDuffColorFilter:PorterDuff混合模式的色彩过滤器。如果你想了解相关知识可以查相关文档,这里就不详细讲了,超出本文范围。Overridepublic ColorFilter getColorFilter() return mColorFilter;解释:获取着色器。到目前位置,整个CircleImageView中的建议使用的公共方法差不多就上述这么些,还有一些现在已经不建议使用了,我就没有拿出来,比如设置图片背景颜色啊等等,已经用注解Deprecated进行了标注,如下:Deprecatedpublic 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, 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()相关知识可以查看如下文章:/buaaroid/article/details/49360779 接着它将mRead置为true,这里想不用管,只知道她在初始化的时候是false就行了。接着往下,判断了一个mSetupPending属性,这个属性因为在一开是false的,所以不会进入括号内,所以更不会调用我们的setup()方法。难道这样就完了吗?不会,因为我们在XML中没有设置图片相关信息,那么我们肯定要在代码里边设置了,那么我们在代码里就需要绑定xml中的View然后调用circleImageView.setImageResource(R.mipmap.psb);我们在上一节中说过在CircleImageView中用四个方法可以设置图片,这个就是其中之一,好吧,我们接着看它的内部实现:Overridepublic 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,然后判断我们是否设置了圆环压住圆形图片并且mBorderWidth0,如下: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() -
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 语谱图课件原理
- 语言区游戏理论知识培训课件
- 2025咨询合同-泄露后果
- 2025企业合作协议协议范本
- 2025员工派遣协议方案借调合同
- 2025房产按揭贷款购买合同
- 团队绩效评估体系评分标准模板
- 互联网技术咨询服务合作合同
- 合作社农田种植项目协议
- 2025年智能制造行业补贴资金申请策略与案例分析报告
- 经济学基础课件 项目三 支付结算法律制度
- 城市低空安全监管平台解决方案
- 员工入职申请表(完整版)
- 销售述职竞聘报告
- 超市安全知识培训内容
- 银行招聘职业能力测验-2025中国银行春招笔试考题
- 630KVA箱变安装工程施工设计方案
- DBJ51T 195-2022 四川省纵向增强体心墙土石坝技术规程
- 农家乐大学生创业计划书
- 《马克思生平故事》课件
- 主动脉夹层临床医学专业教学系列课件讲解
评论
0/150
提交评论