先看效果图:
现在大部分的app上难免会使用到圆形头像,所以今天我给大家分享一个单独使用的,并且周围带有圆环动画的花哨圆形头像控件,本控件是在圆形头像控件基础上实现的,只是在其周围再画一些不同大小的圆而已,就可以实现如图的效果。
圆形头像的基本原理是将设置的资源文件转化成Bitmap,然后通过BitmapShader类将Bitmap成为Paint的渲染器,然后在onDraw()中通过canvas.drawCircle(rx,ry,radius,paint);画布上画圆,而这个圆就是形成了圆形头像。
在xml中通过src设置的资源文件在ImageVIEw的setimageDrawable(drawable)方法中可以得到Drawable类型的图像,然后再将Drawable转成Bitmap就可以了
public voID setimageDrawable(Drawable drawable) { super.setimageDrawable(drawable); mBitmap = getBitmapFromDrawable(drawable); setup(); } private Bitmap getBitmapFromDrawable(Drawable drawable) { if (drawable == null) { return null; } if (drawable instanceof BitmapDrawable) { //从bitmap中间裁剪出最大的正方形 Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); return getMaxSquareCenter(bitmap); //return ((BitmapDrawable) drawable).getBitmap(); } try { Bitmap bitmap; if (drawable instanceof colorDrawable) { bitmap = Bitmap.createBitmap(colorDRAWABLE_DIMENSION,colorDRAWABLE_DIMENSION,BITMAP_CONfig); } else { int min = Math.min(drawable.getIntrinsicWIDth(),drawable.getIntrinsicHeight()); bitmap = Bitmap.createBitmap(min,min,BITMAP_CONfig); } Canvas canvas = new Canvas(bitmap); int left,top,right,buttom; int wIDth = canvas.getWIDth(); int height = canvas.getHeight(); int abs = Math.abs(wIDth - height); if(wIDth <= height){ left = 0; top = (height - abs) / 2; right = wIDth; buttom = height - top; }else{ left = (wIDth - abs) / 2; top = 0; right = wIDth - left; buttom = height; } //drawable.setBounds(0,canvas.getWIDth(),canvas.getHeight()); drawable.setBounds(left,buttom); drawable.draw(canvas); return bitmap; } catch (OutOfMemoryError e) { return null; } } /** * 从bitmap中间裁剪出最大的正方形 * @param bitmap * @return */ private Bitmap getMaxSquareCenter(Bitmap bitmap){ int w = bitmap.getWIDth(); // 得到图片的宽,高 int h = bitmap.getHeight(); int cropWIDth = w >= h ? h : w;// 裁切后所取的正方形区域边长 return Bitmap.createBitmap(bitmap,(w - cropWIDth)/2,(h - cropWIDth)/2,cropWIDth,null,false); }
获取到Bitmap对象后就可以将Bitmap缩小一倍后,在将其画在画布上,这样就有地方来画周围的圆环了。
private voID updateShaderMatrix() { float scale; float dx = 0; float dy = 0; mShaderMatrix.set(null); if (mBitmapWIDth * mDrawableRect.height() > mDrawableRect.wIDth() * mBitmapHeight) { scale = mDrawableRect.height() / (float) mBitmapHeight / 2; //将图片缩放在正中间 缩小一倍 dx = (mDrawableRect.wIDth() - mBitmapWIDth * scale) * 0.5f; } else { scale = mDrawableRect.wIDth() / (float) mBitmapWIDth / 2; //将图片缩放在正中间 缩小一倍 dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f; } mShaderMatrix.setScale(scale,scale ); //在x轴上平移mDrawableRadius,就在正中间了 mShaderMatrix.postTranslate((int) (dx + 0.5f) + mborderWIDth + mDrawableRadius,(int) (dy + 0.5f) + mborderWIDth); mBitmapShader.setLocalMatrix(mShaderMatrix); }
下面就是画图片周围的圆环了,就是在图片的外围画两个圆,一个半径大点,颜色浅点,一个半径小点,颜色深点就可以了,然后通过Handler通过延时 *** 作,不断的改变两个圆的半径大小和颜色的深浅,重绘就可以了
private float mChangeRateborder;//记录外圆执行动画时半径变化率 private float mChangeRateOuter;//记录内圆执行动画时半径变化率 private float mChangeRateInner;//记录图片边框执行动画时半径变化率 private float mChangeRange;//变化范围,VIEw半径的1/6 //*******执行动画*******// //外圆执行动画时半径变化率 private float mRateOuter[] = { -1,-1,0.1f,0.2f,0.3f,0.4f,0.5f,0.6f,0.7f,0.8f,0.9f,1.0f,0.92f,0.88f,0.85f,0.82f,0.76f,0.72f,0.68f,0.60f,0.54f,0.48f,0.40f,0.33f,0.28f,0.20f}; //内圆执行动画时半径变化率 private float mRateInner[] = { -1,0.84f,0.80f,0.67f,-1f,-1f}; //图片边框执行动画时半径变化率 private float mRateborder[] = { 0,0.90f,0.78f,0.64f,0.58f,-1f}; private int mRateIndex;//动画变化率的索引
Handler不断改变半径的变化率和画笔的颜色
/** * 按住执行动画 */ private Handler mHandler = new Handler(){ @OverrIDe public voID handleMessage(Message msg) { super.handleMessage(msg); int index = mRateIndex ++; mChangeRateborder = mRateborder[(index)% mRateborder.length]; setPaintCorlor(mborderPaint,mChangeRateborder,DEFAulT_border_color); setPaintAlpha(mborderPaint,(index)% mRateborder.length,mRateborder); mChangeRateOuter = mRateOuter[(index) % mRateOuter.length]; setPaintCorlor(mOuterPaint,mChangeRateOuter,mOuterPaintcolor); setPaintAlpha(mOuterPaint,(index) % mRateOuter.length,mRateOuter); mChangeRateInner = mRateInner[(index) % mRateInner.length]; setPaintCorlor(mInnerPaint,mChangeRateInner,mInnerPaintcolor); setPaintAlpha(mInnerPaint,(index) % mRateInner.length,mRateInner); //System.out.println("---------mChangeRate:"+mChangeRateborder); invalIDate(); mHandler.removeCallbacksAndMessages(null); mHandler.sendEmptyMessageDelayed(0,30); } };
每执行一次handleMessage()就会触发(invalIDate())VIEw重绘,onDraw中不断的重绘,只需改变每个Circle的半径即可
@OverrIDe protected voID onDraw(Canvas canvas) { if (getDrawable() == null) { return; } //画动画的图形 canvas.drawCircle(getWIDth() / 2,getHeight() / 2,getChangeRadiusOuter(mOuterRadius),mOuterPaint); canvas.drawCircle(getWIDth() / 2,getChangeRadiusInner(mInnerRadius),mInnerPaint); canvas.drawCircle(getWIDth() / 2,mDrawableRadius,mBitmapPaint); canvas.drawCircle(getWIDth() / 2,getChangeRadiusborder(mborderRadius),mborderPaint); }
根据Handler中给的变化率来计算每个Circle的半径
private float getChangeRadiusborder(float radius){ return mChangeRateborder * mChangeRange + radius; } private float getChangeRadiusOuter(float radius){ return mChangeRateOuter * mChangeRange + radius; } private float getChangeRadiusInner(float radius){ return mChangeRateInner * mChangeRange + radius; }
这样在不断的向Handler发送消息时,也就会不断的触发重绘,不断改变外围圆的大小,形成上图所见的动画效果
如下四个方法,就是来控制圆环所展示的不同状态
/** * 开始动画 */ public voID startAnim(){ //让外围的圆环动起来 mHandler.sendEmptyMessageDelayed(0,30); } /** * 停止动画 */ public voID stopAnim(){ //停止外圆环的动画,展示默认的大小 mHandler.removeCallbacksAndMessages(null); mChangeRateborder = 0; mChangeRateOuter = 0; mChangeRateInner = 0; initAnimcolor(); enteranim(); } /** * 进入动画 */ public voID enteranim(){ //展示外圆环,显示默认大小 mHandlerEnter.sendEmptyMessage(0); } /** * 退出动画 */ public voID exitAnim(){ //隐藏外圆环 mHandlerExit.sendEmptyMessage(0); }
好了,动态头像到这基本就实现了,进入动画和退出动画的代码就不贴出来分析了,如果发现BUG或者有任何意见欢迎留言。
完整源码
import androID.content.Context; import androID.graphics.Bitmap; import androID.graphics.BitmapShader; import androID.graphics.Canvas; import androID.graphics.color; import androID.graphics.Matrix; import androID.graphics.Paint; import androID.graphics.RectF; import androID.graphics.Shader; import androID.graphics.drawable.BitmapDrawable; import androID.graphics.drawable.colorDrawable; import androID.graphics.drawable.Drawable; import androID.os.Handler; import androID.os.Message; import androID.util.AttributeSet; import androID.Widget.ImageVIEw; /** * Created by cj_28 on 2016/10/15. */ public class DynamicAvatarVIEw extends ImageVIEw { private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP; private static final int colorDRAWABLE_DIMENSION = 1; private static final Bitmap.Config BITMAP_CONfig = Bitmap.Config.ARGB_8888; private Bitmap mBitmap; private BitmapShader mBitmapShader; private final Matrix mShaderMatrix = new Matrix(); private final Paint mBitmapPaint = new Paint(); private final Paint mborderPaint = new Paint(); private final Paint mOuterPaint = new Paint(); private final Paint mInnerPaint = new Paint(); private static final int DEFAulT_border_WIDTH = 3; private static final int DEFAulT_border_color = color.WHITE; private static final int OUTER_PAINT_color = color.parsecolor("#55FFFFFF"); private static final int INNER_PAINT_color = color.parsecolor("#66FFFFFF"); private int mbordercolor = DEFAulT_border_color; private int mborderWIDth = DEFAulT_border_WIDTH; private int mOuterPaintcolor = OUTER_PAINT_color; private int mInnerPaintcolor = INNER_PAINT_color; private int mBitmapWIDth; private int mBitmapHeight; private final RectF mDrawableRect = new RectF(); private final RectF mborderRect = new RectF(); private float mDrawableRadius;//显示的图片 private float mborderRadius;//..//显示的图片上的边框 private float mOuterRadius;//外层动画 private float mInnerRadius;//..//内层动画 private float mRealDrawableRadius;//这是VIEw没有被缩放之前的mDrawableRadius的半径 private float mRealborderRadius;//.. private boolean mReady; private boolean mSetupPending; private float mChangeRateborder;//记录外圆执行动画时半径变化率 private float mChangeRateOuter;//记录内圆执行动画时半径变化率 private float mChangeRateInner;//记录图片边框执行动画时半径变化率 private float mChangeRange;//变化范围,VIEw半径的1/6 //*******执行动画*******// //外圆执行动画时半径变化率 private float mRateOuter[] = { -1,-1f}; //private int mcolor[] = {0x55FFFFFF,0x44FFFFFF,0x33FFFFFF,0x22FFFFFF,0x11FFFFFF,0x00FFFFFF,0x00FFFFFF}; private int mRateIndex;//动画变化率的索引 //*******进入动画*******// //外圆执行动画时半径变化率 private float mRateOuterEnter[] = { -2,-2,//外圆要缩小2个mChangeRange才会完全隐藏 -2,-0.8f,-0.6f,-0.4f,-0.2f,-0.1f,0.44f,0.35f,0.25f,0.20f,0.15f,0.0f}; //内圆执行动画时半径变化率 private float mRateInnerEnter[] = { -1,//外圆要缩小1个mChangeRange才会完全隐藏 -0.8f,0.45f,0.05f,0f,0f}; private int mRateIndexEnter;//进入动画变化率的索引 //*******退出动画*******// //外圆执行动画时半径变化率 private float mRateOuterExit[] = { 0.0f,-1.2f,-1.4f,-1.6f,-1.8f,-2f}; //内圆执行动画时半径变化率 private float mRateInnerExit[] = { 0.0f,-0.3f,-0.5f,-0.7f,-0.9f,-1f}; private int mRateIndexExit;//进入动画变化率的索引 /** * 按住执行动画 */ private Handler mHandler = new Handler(){ @OverrIDe public voID handleMessage(Message msg) { super.handleMessage(msg); int index = mRateIndex ++; mChangeRateborder = mRateborder[(index)% mRateborder.length]; setPaintCorlor(mborderPaint,30); } }; private Handler mHandlerEnter = new Handler(){ @OverrIDe public voID handleMessage(Message msg) { super.handleMessage(msg); int index = mRateIndexEnter ++; if(index >= mRateOuterEnter.length) { mRateIndexEnter = 0; mHandlerEnter.removeCallbacksAndMessages(null); return; } mChangeRateOuter = mRateOuterEnter[(index) % mRateOuterEnter.length]; mChangeRateInner = mRateInnerEnter[(index) % mRateInnerEnter.length]; invalIDate(); mHandlerEnter.removeCallbacksAndMessages(null); mHandlerEnter.sendEmptyMessageDelayed(0,20); } }; private Handler mHandlerExit = new Handler(){ @OverrIDe public voID handleMessage(Message msg) { super.handleMessage(msg); int index = mRateIndexExit ++; if(index >= mRateOuterExit.length) { mRateIndexExit = 0; mHandlerExit.removeCallbacksAndMessages(null); return; } mChangeRateOuter = mRateOuterExit[(index) % mRateOuterExit.length]; mChangeRateInner = mRateInnerExit[(index) % mRateInnerExit.length]; invalIDate(); mHandlerExit.removeCallbacksAndMessages(null); mHandlerExit.sendEmptyMessageDelayed(0,20); } }; /** * 设置outer和inner的画笔颜色 * @param paint * @param rate * @param color */ private voID setPaintCorlor(Paint paint,float rate,int color){ if(rate < 0){ paint.setcolor(color.transparent); }else{ paint.setcolor(color); } } /** * 设置透明度 * @param paint * @param index * @param rate */ private voID setPaintAlpha(Paint paint,int index,float[] rate){ int pre = index -1; if(pre >= 0 ){ if(rate[pre] > rate[index] && rate[index] > 0){ int color = paint.getcolor(); int colortransparent = color & 0xff000000; int colorValue = color & 0x00ffffff; colortransparent = colortransparent >>> 7; paint.setcolor((int)(rate[index] * colortransparent) << 7 | colorValue); } } } public DynamicAvatarVIEw(Context context) { this(context,null); } public DynamicAvatarVIEw(Context context,AttributeSet attrs) { this(context,attrs,0); } public DynamicAvatarVIEw(Context context,AttributeSet attrs,int defStyleAttr) { super(context,defStyleAttr); super.setScaleType(SCALE_TYPE); //可以执行了 mReady = true; if (mSetupPending) { setup(); mSetupPending = false; } } @OverrIDe protected voID onMeasure(int wIDthMeasureSpec,int heightmeasureSpec) { int wIDthMode = MeasureSpec.getMode(wIDthMeasureSpec); int wIDthSize = MeasureSpec.getSize(wIDthMeasureSpec); int heightmode = MeasureSpec.getMode(heightmeasureSpec); int heightSize = MeasureSpec.getSize(heightmeasureSpec); int size = Math.min(wIDthSize,heightSize); super.onMeasure(MeasureSpec.makeMeasureSpec(size,wIDthMode),MeasureSpec.makeMeasureSpec(size,heightmode)); } @OverrIDe protected voID onLayout(boolean changed,int left,int top,int right,int bottom) { super.onLayout(changed,left,bottom); enteranim(); } @OverrIDe protected voID onDraw(Canvas canvas) { if (getDrawable() == null) { return; } //画动画的图形 canvas.drawCircle(getWIDth() / 2,mborderPaint); } private float getChangeRadiusborder(float radius){ return mChangeRateborder * mChangeRange + radius; } private float getChangeRadiusOuter(float radius){ return mChangeRateOuter * mChangeRange + radius; } private float getChangeRadiusInner(float radius){ return mChangeRateInner * mChangeRange + radius; } private voID initAnimcolor(){ mOuterPaint.setStyle(Paint.Style.FILL); mOuterPaint.setAntiAlias(true); mOuterPaint.setcolor(mOuterPaintcolor); mInnerPaint.setStyle(Paint.Style.FILL); mInnerPaint.setAntiAlias(true); mInnerPaint.setcolor(mInnerPaintcolor); //图片边框(默认白色) mborderPaint.setcolor(mbordercolor); mOuterRadius = mRealborderRadius / 6 * 5; mInnerRadius = mRealborderRadius / 6 * 4; mChangeRange = mRealborderRadius / 6; mRateIndex = 0; } /** * 开始动画 */ public voID startAnim(){ mHandler.sendEmptyMessageDelayed(0,30); } /** * 停止动画 */ public voID stopAnim(){ mHandler.removeCallbacksAndMessages(null); mChangeRateborder = 0; mChangeRateOuter = 0; mChangeRateInner = 0; // mborderPaint.setcolor(DEFAulT_border_color); // mOuterPaint.setcolor(mOuterPaintcolor); // mInnerPaint.setcolor(mInnerPaintcolor); // mRateIndex = 0; initAnimcolor(); //invalIDate(); enteranim(); } /** * 进入动画 */ public voID enteranim(){ mHandlerEnter.sendEmptyMessage(0); } /** * 退出动画 */ public voID exitAnim(){ mHandlerExit.sendEmptyMessage(0); } /** * 设置外圆动画的颜色 * @param outerPaintcolor */ public voID setouterPaintcolor(int outerPaintcolor) { if (outerPaintcolor == mOuterPaintcolor) { return; } mOuterPaintcolor = outerPaintcolor; mOuterPaint.setcolor(mOuterPaintcolor); invalIDate(); } /** * 设置内圆动画的颜色 * @param innerPaintcolor */ public voID setInnerPaintcolor(int innerPaintcolor) { if (innerPaintcolor == mInnerPaintcolor) { return; } mInnerPaintcolor = innerPaintcolor; mInnerPaint.setcolor(mInnerPaintcolor); invalIDate(); } /** * 设置图片边框的颜色 * @param bordercolor */ public voID setbordercolor(int bordercolor) { if (bordercolor == mbordercolor) { return; } mbordercolor = bordercolor; mborderPaint.setcolor(mbordercolor); invalIDate(); } /** * 设置图片边框的宽度 * @param borderWIDth */ public voID setborderWIDth(int borderWIDth) { if (borderWIDth == mborderWIDth) { return; } mborderWIDth = borderWIDth; setup(); } @OverrIDe public ScaleType getScaleType() { return SCALE_TYPE; } @OverrIDe public voID setScaleType(ScaleType scaleType) { if (scaleType != SCALE_TYPE) { throw new IllegalArgumentException(String.format("ScaleType %s not supported.",scaleType)); } } @OverrIDe protected voID onSizeChanged(int w,int h,int olDW,int oldh) { super.onSizeChanged(w,h,olDW,oldh); //只有在此方法中调用setup,setup中的getWIDth方法得到的值才不会是0, setup(); } @OverrIDe public voID setimageBitmap(Bitmap bm) { super.setimageBitmap(bm); mBitmap = getMaxSquareCenter(bm); setup(); } /** * mxl中设置src就会走此方法 * @param drawable */ @OverrIDe public voID setimageDrawable(Drawable drawable) { super.setimageDrawable(drawable); mBitmap = getBitmapFromDrawable(drawable); setup(); } @OverrIDe public voID setimageResource(int resID) { super.setimageResource(resID); mBitmap = getBitmapFromDrawable(getDrawable()); setup(); } /** * 从bitmap中间裁剪出最大的正方形 * @param bitmap * @return */ private Bitmap getMaxSquareCenter(Bitmap bitmap){ int w = bitmap.getWIDth(); // 得到图片的宽,高 int h = bitmap.getHeight(); int cropWIDth = w >= h ? h : w;// 裁切后所取的正方形区域边长 return Bitmap.createBitmap(bitmap,false); } private Bitmap getBitmapFromDrawable(Drawable drawable) { if (drawable == null) { return null; } if (drawable instanceof BitmapDrawable) { //从bitmap中间裁剪出最大的正方形 Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); return getMaxSquareCenter(bitmap); //return ((BitmapDrawable) drawable).getBitmap(); } try { Bitmap bitmap; if (drawable instanceof colorDrawable) { bitmap = Bitmap.createBitmap(colorDRAWABLE_DIMENSION,buttom); drawable.draw(canvas); return bitmap; } catch (OutOfMemoryError e) { return null; } } private voID setup() { //只有执行过构造函数之后,所有的成员才被初始化完毕 if (!mReady) { mSetupPending = true; return; } if (mBitmap == null) { return; } 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(); //图片边框设置的范围 mborderRect.set(0,getWIDth(),getHeight()); mborderRadius = Math.min((mborderRect.height() - mborderWIDth) / 2,(mborderRect.wIDth() - mborderWIDth) / 2) / 2; mRealborderRadius = 2 * mborderRadius; //图片显示的范围 mDrawableRect.set(mborderWIDth,mborderWIDth,mborderRect.wIDth() - mborderWIDth,mborderRect.height() - mborderWIDth); //让图片显示的范围是控件大小的一半 mDrawableRadius = Math.min(mDrawableRect.height() / 2,mDrawableRect.wIDth() / 2) / 2; mRealDrawableRadius = 2 * mDrawableRadius; updateShaderMatrix(); initAnimcolor(); invalIDate(); } private voID updateShaderMatrix() { float scale; float dx = 0; float dy = 0; mShaderMatrix.set(null); if (mBitmapWIDth * mDrawableRect.height() > mDrawableRect.wIDth() * mBitmapHeight) { scale = mDrawableRect.height() / (float) mBitmapHeight / 2; //将图片缩放在正中间 dx = (mDrawableRect.wIDth() - mBitmapWIDth * scale) * 0.5f; } else { scale = mDrawableRect.wIDth() / (float) mBitmapWIDth / 2; dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f; } mShaderMatrix.setScale(scale,scale ); mShaderMatrix.postTranslate((int) (dx + 0.5f) + mborderWIDth + mDrawableRadius,(int) (dy + 0.5f) + mborderWIDth); mBitmapShader.setLocalMatrix(mShaderMatrix); } }
参考:https://github.com/hdodenhof/CircleImageView
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。
总结以上是内存溢出为你收集整理的Android实现动态圆环的图片头像控件全部内容,希望文章能够帮你解决Android实现动态圆环的图片头像控件所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)