新浦京81707con > 功能介绍 > 8455新葡萄娱乐CircleImageView源码剖析,CircleImageV

原标题:8455新葡萄娱乐CircleImageView源码剖析,CircleImageV

浏览次数:117 时间:2020-03-15

———世袭自AppCompatImageView,未有增大属性,可像ImageView相似接收。

深信广大人都用过此控件:

CircleImageView的做事原理

如需下载源码,请访谈

8455新葡萄娱乐 1Paste_Image.png

花色地址:CircleImageView

小说原创,转发请申明出处:史上最简练高效的圆形ImageView

怎么使用就不在这样叙述了,直接选用的观客能够到此处

在深入深入分析CircleImageView源码以前,先读书一些知识点

运作效果如下:

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

8455新葡萄娱乐 2circle_imageview.png

本文从自定义View学习的角度出发剖判作者是如何编写那类型控件的。

  • BitmapShader
  • ComposeShader
  • LinearGradient
  • RadialGradient
  • SweepGradient
贯彻思路:

自定义CircleImageView继承自AppCompatImageView,重写onDraw方法,

通过BitmapDrawable的方法

 drawable.getBitmap()

获取ImageView所体现图片的Bitmap

透过此Bitmap创造四个着色器BitmapShader,

 BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

基于图片Bitmap的宽高和控件CircleImageView的宽高,通过缩放矩阵Matrix对BitmapShader举行适宜缩放,使图片撑满控件

 float scale = Math.max(width / bitmap.getWidth(), height / bitmap.getHeight; matrix.setScale(scale, scale);//将图片宽高等比例缩放,避免拉伸 bitmapShader.setLocalMatrix;

下一场将着色器设置给画笔Paint,并通过Canvas的drawCircle方法,以控件中央为圆心画内切圆。

 paint.setShader(initBitmapShader((BitmapDrawable) drawable));//将着色器设置给画笔 canvas.drawCircle(width / 2, height / 2, radius, paint);//使用画笔在画布上画圆

自定义View的分类:

大略自定义View分为三类:直接接轨View,拓宽系统已有些View,世襲ViewGroup或此外ViewGroup的子类。废话少说,开头剖判此类。该控件就这样多个类;所以算相对轻巧的。

先是来认知一下Shader是何许?

完全代码如下:
package com.edward.feng.circleimageview.view;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapShader;import android.graphics.Canvas;import android.graphics.Matrix;import android.graphics.Paint;import android.graphics.Shader;import android.graphics.drawable.BitmapDrawable;import android.support.annotation.Nullable;import android.support.v7.widget.AppCompatImageView;import android.util.AttributeSet;/** * 功能描述:一个简洁而高效的圆形ImageView * * @author  edward * @link http://www.jianshu.com/u/f7176d6d53d2 * 创建时间: 2018/4/17 0017 */public class CircleImageView extends AppCompatImageView { private float width; private float height; private float radius; private Paint paint; private Matrix matrix; public CircleImageView(Context context) { this(context, null); } public CircleImageView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public CircleImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); paint = new Paint(); paint.setAntiAlias; //设置抗锯齿 matrix = new Matrix(); //初始化缩放矩阵 } /** * 测量控件的宽高,并获取其内切圆的半径 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = getMeasuredWidth(); height = getMeasuredHeight(); radius = Math.min(width, height) / 2; } @Override protected void onDraw(Canvas canvas) { Drawable drawable = getDrawable(); if (drawable == null) { super.onDraw; return; } if (drawable instanceof BitmapDrawable) { paint.setShader(initBitmapShader((BitmapDrawable) drawable));//将着色器设置给画笔 canvas.drawCircle(width / 2, height / 2, radius, paint);//使用画笔在画布上画圆 return; } super.onDraw; } /** * 获取ImageView中资源图片的Bitmap,利用Bitmap初始化图片着色器,通过缩放矩阵将原资源图片缩放到铺满整个绘制区域,避免边界填充 */ private BitmapShader initBitmapShader(BitmapDrawable drawable) { Bitmap bitmap = drawable.getBitmap(); BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); float scale = Math.max(width / bitmap.getWidth(), height / bitmap.getHeight; matrix.setScale(scale, scale);//将图片宽高等比例缩放,避免拉伸 bitmapShader.setLocalMatrix; return bitmapShader; }}

CircleImageView布局函数

8455新葡萄娱乐 3Paste_Image.png

后续ImageView基本上海重型机器厂写七个构造函数,其实正是调用多个参数的百般,都以套路呀。。。

public CircleImageView(Context context){ super; init();}public CircleImageView(Context context, AttributeSet attrs){ this(context,attrs,0);}public CircleImageView(Context context,AttributeSet attrs,int defStyle){ super(context,attrs,defStyle); 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;}

我们看看Image源码的结构函数:

8455新葡萄娱乐 4Paste_Image.png

都以同一个套路:

直白翻译过来叫着色器,没了然过的人看了会深感很蒙,就跟把Socket翻译成套接字似的,现实生活在跟本未有那玩意啊,怎么精晓呢?

在xml构造文件中,直接当imageview使用就能够
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:andro xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffeeeeff" android:orientation="vertical" android:padding="5dp" tools:context=".MainActivity"> <com.edward.feng.circleimageview.view.CircleImageView android: android:layout_width="100dp" android:layout_height="100dp" android:src="@mipmap/natural_beauty_01" android:contentDescription="@string/app_name"/> <com.edward.feng.circleimageview.view.CircleImageView android: android:layout_width="130dp" android:layout_height="130dp" android:src="@mipmap/natural_beauty_02" android:contentDescription="@string/app_name" android:layout_marginTop="93dp"/> <com.edward.feng.circleimageview.view.CircleImageView android: android:layout_width="200dp" android:layout_height="200dp" android:src="@mipmap/natural_beauty_03" android:layout_below="@id/girls02" android:contentDescription="@string/app_name"/> <com.edward.feng.circleimageview.view.CircleImageView android: android:layout_width="250dp" android:layout_height="250dp" android:src="@mipmap/natural_beauty_05" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" android:contentDescription="@string/app_name"/> <com.edward.feng.circleimageview.view.CircleImageView android: android:layout_width="170dp" android:layout_height="170dp" android:src="@mipmap/natural_beauty_04" android:layout_below="@id/girls04" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" android:contentDescription="@string/app_name"/></RelativeLayout>

拜谒五个参数的布局函数到底干了些什么

public CircleImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); 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;}

犹如此些,一句句给大家逐个解答:

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);

此行意思是从xml布局文件中获取自定义view类型的贰个类型数组;其实大家自定义view有个别时候会自定义一些性质,就能在二个xml文件上先写好内需定义什么性质,那一个天性的品种是怎么样,供结构的xml调用者使用;

8455新葡萄娱乐 5Paste_Image.png8455新葡萄娱乐 6Paste_Image.png

该CircleImageView就自定义了这个属性。看看这里的xml的declare-styleable中的name属性和昂科威.styleable.CircleImageView是雷同的。

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);

那四行为主均等就投身一齐讲,正是通过刚才概念的种类数组获取各自赢得的数值,如果没有得到布局xml文件的数值就能提供三个暗中认可值给它。那多少个点子都以七个参数,第二个参数正是暗中同意值,而首先个就是结构xml文件调用者写的值;

此处说过题外话:对于获得dp或sp值的,基本上都是这么书写,原因是安卓通过dp或sp获取到相应的px。在java代码里面获取到的都以px为单位的。而在布局xml是dp或sp:

 TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,16,getResources().getDisplayMetricsTypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,1,getResources().getDisplayMetrics

这种写法正是给默许值的数值调换为dp或sp单位的。

a.recycle();

该方式正是获取完xml构造文件的习性都亟需将TypeArray回笼,方便之后采用。ImageView源码亦都完全一样,所以官方的源码必定要多看呀。。。

由此作者稳重的商量,小编觉着Shader就好像一把刷子可能说是印章,它调节写出来的东西是何许颜色的,可能画出到图案是什么体统到。

布局函数看完再看它最后调用的一个方法init(卡塔尔(قطر‎

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

调用父类的setScaleType为CircleImageView设置缩放计策。该CircleImageView只扶助ScaleType.CENTELAND_CROP这种缩放战术:意思是将图纸等比例缩放在View上,不会对其举行拉伸填满View;大白话便是图表都会显得在View上,倘若图片比例抢先View即剪切多余部分显得正中间部分;别的的缩放计谋能够看看此介绍,个人感觉说得正确,这里就不描述:

再看看源码为啥说该控件只协理ScaleType.CENTE大切诺基_CROP属性:

private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;super.setScaleType(SCALE_TYPE);@Overridepublic void setScaleType(ScaleType scaleType) { if(scaleType != SCALE_TYPE){ throw new IllegalArgumentException(String.format("ScaleType %s not supported.",scaleType)); }}

CircleImageView重写了ImageView的setScaleType方法;只要用别样的scaleType即报错

那边有四个变量:mReady和mSetupPending;它们是用来支配setup方法被调用的。

这里供给解释一切CircleImageView的调用流程,之后再将相继方法开展教学。

比方作者想要制作一台车,在全体都建构实现之后,加了油就能够开了,然则看起来怎么那样不狂拽炫吊炸天呢? 哦对了,忘了给车喷漆了,当时我们就拿几喷漆罐对着车喷一些文字或图案,那喷图案的经过正是Shader的进程.

全部CircleImageView的调用流程
  • 1、由于是一而再ImageView,所以先调用父类的构造函数,当调用ImageView的布局函数是调用了setImageDrawable方法 --->setup方法 mReady为false,mSetupPending为true,那样只好到setup第三个判定
  • initializeBitmap(卡塔尔(قطر‎方法通过getBitmapFromDrawable(State of Qatar方法得到bitmap

  • 2、再走结构方法 --> init(State of Qatar方法 由于现行 还不曾调用onMeasure ,所以通过不断第壹个剖断 ,以往的宽高是0

  • 3、onMeasure方法被调用,数次被调用,得到宽高

  • 4、获得宽高后,onSizeChange(卡塔尔(قطر‎被调用 先调用父类的,然后再次被调用onMeasure---> 再次调用setup方法

  • mBorderRect设置矩形区域,mFillRect设置矩形区域;mBorderRadiusmFillRadius设置半径

  • 5、在setup() 调用applyColorFilter() 和updateShaderMatrix()

  • 6、invalidate(卡塔尔(قطر‎ 重绘调用onDraw(卡塔尔(قطر‎方法

全体流程大致正是如此。大概这么看不是太了然,看官们能够在源码上debug看看是还是不是那样的。究竟“浮光掠影”

简轻松单的话setup方法首要规定CircleImageView所占的限制,图片怎么样在CircleImageView呈现。以往每行代码深入分析:

if{//第一次调用走这里 mSetupPending = true; return; } if(getWidth() == 0 && getHeight{ return; } if(mBitmap == null){ invalidate(); return; }

这个都非常少谈正是用来非空决断和局地垄断流程的论断。

mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP,Shader.TileMode.CLAMP); mBitmapPaint.setAntiAlias; mBitmapPaint.setShader(mBitmapShader);

BitmapShader正是一个位图着色器,简单来说正是将图纸呈今后canvas画布中。Shader.TileMode.CLAMP该属性是图片最终贰个像素实行拉伸管理;日常用此属性居多,其它多个分别是:REPEAT :横向和纵向的重新渲染器图片,平铺。MI酷威ROWrangler:横向和纵向的重复渲染器图片,那几个和REPEAT重复方式不平等,他是以镜像情势平铺。mBitmapPaint.setAntiAlias;正是抗锯齿mBitmapPaint.setShader(mBitmapShader卡塔尔(قطر‎;将画笔设置bitmapShader

 mBorderPaint.setStyle(Paint.Style.STROKE); mBorderPaint.setAntiAlias; mBorderPaint.setColor(mBorderColor); mBorderPaint.setStrokeWidth(mBorderWidth);

CircleImageView分别有多个画笔分别上面代码浮现的承负图片mBitmapPaint,担当边界的即这里的mBorderPaint,还应该有担任填充的mFillPaint;上边四行代码正是给边界画笔作策动,希图画二个空心圆。mBorderPaint.setStyle(Paint.Style.STROKE卡塔尔;mBorderPaint.setStrokeWidth(mBorderWidth卡塔尔国;这两行便是安装画笔空心和画笔的粗度

 mFillPaint.setStyle(Paint.Style.FILL); mFillPaint.setAntiAlias; mFillPaint.setColor(mFillColor);

为填充画笔设置属性

 mBitmapHeight = mBitmap.getHeight(); mBitmapWidth = mBitmap.getWidth();

赢得位图的宽高

 //设置边界矩形这个矩形的区域 mBorderRect.set(calculateBounds; //取边界矩形宽高小的为半径 mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f); //初始图片显示区域为mBorderRect(CircleImageView中图片区域的实际大小) mDrawableRect.set(mBorderRect); if(!mBorderOverlay && mBorderWidth > 0){ //这里是当存在边界的情况将mDrawableRect的矩形区域缩小,注意这里获取到的mBorderWidth是px单位的非xml中的dp mDrawableRect.inset(mBorderWidth - 1.0f,mBorderWidth - 1.0f); } //取图片显示区域的矩形的半径,该半径是少于或等于边界矩形的半径,因为存在边界即少于,不存在即等于 mDrawableradius = Math.min(mDrawableRect.height()/2.0f,mDrawableRect.width;

setup方法说罢再看看个中总计矩形区域与矩阵调整缩放的艺术

 private RectF calculateBounds(){ //出去内边距获取真正显示图片的区域 int availableWidth = getWidth() - getPaddingLeft() - getPaddingRight(); int availableHeight = getHeight() - getPaddingBottom() - getPaddingTop(); //取长宽之间较小的那个长度 int sideLength = Math.min(availableWidth,availableHeight); //这里计算的左坐标与上的坐标,其实就是为了左右居中或者上下居中。让图片始终显示在view的中间 float left = getPaddingLeft()   (availableWidth - sideLength) / 2f; float top = getPaddingTop()   (availableHeight - sideLength) / 2f; //最后返回一个矩形 return new RectF(left,top,left   sideLength,top   sideLength);}

事实上该方式便是用ImageView中CENTE大切诺基_CROP情势的算法,看看ImageView中CENTEENCORE_CROP的代码:

8455新葡萄娱乐 7Paste_Image.png

再看看CircleImageView的updateShaderMatrix()方法:

private void updateShaderMatrix(){ float scale; float dx = 0; float dy = 0; mShaderMatrix.set; if(mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight){ scale = mDrawableRect.height() /  mBitmapHeight; dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f; } else { scale = mDrawableRect.width() / mBitmapWidth; dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f; } mShaderMatrix.setScale(scale,scale); mShaderMatrix.postTranslate(dx   0.5f)   mDrawableRect.left,(dy   0.5f)   mDrawableRect.top); // 设置变换矩阵 mBitmapShader.setLocalMatrix(mShaderMatrix);}

看出来了呢,同出一辙的,所以源码是成都百货上千读书借鉴的地点的

我们位置提到有5种Shader:

本文由新浦京81707con发布于功能介绍,转载请注明出处:8455新葡萄娱乐CircleImageView源码剖析,CircleImageV

关键词: 新浦京81707con 源码 自定义 圆形 高效

上一篇:确保安全,双向通信

下一篇:没有了