Android圆角头像工具类详解
程序员文章站
2022-04-06 13:29:57
很多android应用都用到了圆角头像,或者一些圆角图片处理起来比较麻烦,直接上圆角图片工具类public class circleimageview extends imageview { // 缩...
很多android应用都用到了圆角头像,或者一些圆角图片处理起来比较麻烦,直接上圆角图片工具类
public class circleimageview extends imageview { // 缩放类型 private static final scaletype scale_type = scaletype.center_crop; 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 boolean default_border_overlay = false; private final rectf mdrawablerect = new rectf(); private final rectf mborderrect = new rectf(); private final matrix mshadermatrix = new matrix(); // 这个画笔最重要的是关联了mbitmapshader // 使canvas在执行的时候可以切割原图片(mbitmapshader是关联了原图的bitmap的) private final paint mbitmappaint = new paint(); // 这个描边,则与本身的原图bitmap没有任何关联, private final paint mborderpaint = new paint(); // 这里定义了 圆形边缘的默认宽度和颜色 private int mbordercolor = default_border_color; private int mborderwidth = default_border_width; private bitmap mbitmap; private bitmapshader mbitmapshader; // 位图渲染 private int mbitmapwidth; // 位图宽度 private int mbitmapheight; // 位图高度 private float mdrawableradius;// 图片半径 private float mborderradius;// 带边框的的图片半径 private colorfilter mcolorfilter; // 初始false private boolean mready; private boolean msetuppending; private boolean mborderoverlay; // 构造函数 public circleimageview(context context) { super(context); init(); } // 构造函数 public circleimageview(context context, attributeset attrs) { this(context, attrs, 0); } /** * 构造函数 */ public circleimageview(context context, attributeset attrs, int defstyle) { super(context, attrs, defstyle); // 通过obtainstyledattributes 获得一组值赋给 typedarray(数组) , // 这一组值来自于res/values/attrs.xml中的name="circleimageview"的declare-styleable中。 typedarray a = context.obtainstyledattributes(attrs, r.styleable.circleimageview, defstyle, 0); // 通过typedarray提供的一系列方法getxxxx取得我们在xml里定义的参数值; // 获取边界的宽度 mborderwidth = a.getdimensionpixelsize( r.styleable.circleimageview_border_size, default_border_width); // 获取边界的颜色 mbordercolor = a.getcolor(r.styleable.circleimageview_border_color, default_border_color); // mborderoverlay = // a.getboolean(r.styleable.circleimageview_border_overlay, // default_border_overlay); // 调用 recycle() 回收typedarray,以便后面重用 a.recycle(); init(); } /** * 作用就是保证第一次执行setup函数里下面代码要在构造函数执行完毕时调用 */ private void init() { // 在这里scaletype被强制设定为center_crop,就是将图片水平垂直居中,进行缩放。 super.setscaletype(scale_type); mready = true; if (msetuppending) { setup(); msetuppending = false; } } @override public scaletype getscaletype() { return scale_type; } /** * 这里明确指出 此种imageview 只支持center_crop 这一种属性 * * @param scaletype */ @override public void setscaletype(scaletype scaletype) { if (scaletype != scale_type) { throw new illegalargumentexception(string.format( "scaletype %s not supported.", scaletype)); } } @override public void setadjustviewbounds(boolean adjustviewbounds) { if (adjustviewbounds) { throw new illegalargumentexception( "adjustviewbounds not supported."); } } @override protected void ondraw(canvas canvas) { // 如果图片不存在就不画 if (getdrawable() == null) { return; } // 绘制内圆形 图片 画笔为mbitmappaint canvas.drawcircle(getwidth() / 2, getheight() / 2, mdrawableradius, mbitmappaint); // 如果圆形边缘的宽度不为0 我们还要绘制带边界的外圆形 边界画笔为mborderpaint if (mborderwidth != 0) { canvas.drawcircle(getwidth() / 2, getheight() / 2, mborderradius, mborderpaint); } } @override protected void onsizechanged(int w, int h, int oldw, int oldh) { super.onsizechanged(w, h, oldw, oldh); setup(); } public int getbordercolor() { return mbordercolor; } public void setbordercolor(int bordercolor) { if (bordercolor == mbordercolor) { return; } mbordercolor = bordercolor; mborderpaint.setcolor(mbordercolor); invalidate(); } public void setbordercolorresource(@colorres int bordercolorres) { setbordercolor(getcontext().getresources().getcolor(bordercolorres)); } 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(); } /** * 以下四个函数都是 复写imageview的setimagexxx()方法 注意这个函数先于构造函数调用之前调用 * * @param bm */ @override public void setimagebitmap(bitmap bm) { super.setimagebitmap(bm); mbitmap = bm; setup(); } @override public void setimagedrawable(drawable drawable) { super.setimagedrawable(drawable); mbitmap = getbitmapfromdrawable(drawable); setup(); } @override public void setimageresource(@drawableres int resid) { super.setimageresource(resid); mbitmap = getbitmapfromdrawable(getdrawable()); setup(); } @override public void setimageuri(uri uri) { super.setimageuri(uri); mbitmap = getbitmapfromdrawable(getdrawable()); setup(); } @override public void setcolorfilter(colorfilter cf) { if (cf == mcolorfilter) { return; } mcolorfilter = cf; mbitmappaint.setcolorfilter(mcolorfilter); invalidate(); } /** * drawable转bitmap * * @param drawable * @return */ private bitmap getbitmapfromdrawable(drawable drawable) { if (drawable == null) { return null; } if (drawable instanceof bitmapdrawable) { // 通常来说 我们的代码就是执行到这里就返回了。返回的就是我们最原始的bitmap return ((bitmapdrawable) drawable).getbitmap(); } try { bitmap bitmap; if (drawable instanceof colordrawable) { bitmap = bitmap.createbitmap(colordrawable_dimension, colordrawable_dimension, bitmap_config); } else { bitmap = bitmap.createbitmap(drawable.getintrinsicwidth(), drawable.getintrinsicheight(), bitmap_config); } canvas canvas = new canvas(bitmap); drawable.setbounds(0, 0, canvas.getwidth(), canvas.getheight()); drawable.draw(canvas); return bitmap; } catch (outofmemoryerror e) { return null; } } /** * 这个函数很关键,进行图片画笔边界画笔(paint)一些重绘参数初始化: * 构建渲染器bitmapshader用bitmap来填充绘制区域,设置样式以及内外圆半径计算等, * 以及调用updateshadermatrix()函数和 invalidate()函数; */ private void setup() { // 因为mready默认值为false,所以第一次进这个函数的时候if语句为真进入括号体内 // 设置msetuppending为true然后直接返回,后面的代码并没有执行。 if (!mready) { msetuppending = true; return; } // 防止空指针异常 if (mbitmap == null) { return; } // 构建渲染器,用mbitmap位图来填充绘制区域 ,参数值代表如果图片太小的话 就直接拉伸 mbitmapshader = new bitmapshader(mbitmap, shader.tilemode.clamp, shader.tilemode.clamp); // 设置图片画笔反锯齿 mbitmappaint.setantialias(true); // 设置图片画笔渲染器 mbitmappaint.setshader(mbitmapshader); // 设置边界画笔样式 mborderpaint.setstyle(paint.style.stroke);// 设画笔为空心 mborderpaint.setantialias(true); mborderpaint.setcolor(mbordercolor); // 画笔颜色 mborderpaint.setstrokewidth(mborderwidth);// 画笔边界宽度 // 这个地方是取的原图片的宽高 mbitmapheight = mbitmap.getheight(); mbitmapwidth = mbitmap.getwidth(); // 设置含边界显示区域,取的是circleimageview的布局实际大小,为方形,查看xml也就是160dp(240px) // getwidth得到是某个view的实际尺寸 mborderrect.set(0, 0, getwidth(), getheight()); // 计算 // 圆形带边界部分(外圆)的最小半径,取mborderrect的宽高减去一个边缘大小的一半的较小值(这个地方我比较纳闷为什么求外圆半径需要先减去一个边缘大小) mborderradius = math.min((mborderrect.height() - mborderwidth) / 2, (mborderrect.width() - mborderwidth) / 2); // 初始图片显示区域为mborderrect(circleimageview的布局实际大小) mdrawablerect.set(mborderrect); if (!mborderoverlay) { // demo里始终执行 // 通过inset方法 // 使得图片显示的区域从mborderrect大小上下左右内移边界的宽度形成区域,查看xml边界宽度为2dp(3px),所以方形边长为就是160-4=156dp(234px) mdrawablerect.inset(mborderwidth, mborderwidth); } // 这里计算的是内圆的最小半径,也即去除边界宽度的半径 mdrawableradius = math.min(mdrawablerect.height() / 2, mdrawablerect.width() / 2); // 设置渲染器的变换矩阵也即是mbitmap用何种缩放形式填充 updateshadermatrix(); // 手动触发ondraw()函数 完成最终的绘制 invalidate(); } /** * 这个函数为设置bitmapshader的matrix参数,设置最小缩放比例,平移参数。 作用:保证图片损失度最小和始终绘制图片正*的那部分 */ 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) { // y轴缩放 x轴平移 使得图片的y轴方向的边的尺寸缩放到图片显示区域(mdrawablerect)一样) scale = mdrawablerect.height() / (float) mbitmapheight; dx = (mdrawablerect.width() - mbitmapwidth * scale) * 0.5f; } else { // x轴缩放 y轴平移 使得图片的x轴方向的边的尺寸缩放到图片显示区域(mdrawablerect)一样) scale = mdrawablerect.width() / (float) mbitmapwidth; dy = (mdrawablerect.height() - mbitmapheight * scale) * 0.5f; } // shaeder的变换矩阵,我们这里主要用于放大或者缩小。 mshadermatrix.setscale(scale, scale); // 平移 mshadermatrix.posttranslate((int) (dx + 0.5f) + mdrawablerect.left, (int) (dy + 0.5f) + mdrawablerect.top); // 设置变换矩阵 mbitmapshader.setlocalmatrix(mshadermatrix); } }
使用方法
<com.xxx.xxx.ui.view.circleimageview android:layout_width="45dp" android:layout_height="45dp" android:src="@mipmap/ic_header" app:border_color="@color/bg_dddddd" android:layout_gravity="center_vertical" android:layout_marginright="10dp" app:border_size="1dp" />
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。