我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android实现动态圆环的图片头像控件

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android实现动态圆环的图片头像控件

先看效果图:

现在大部分的app上难免会使用到圆形头像,所以今天我给大家分享一个单独使用的,并且周围带有圆环动画的花哨圆形头像控件,本控件是在圆形头像控件基础上实现的,只是在其周围再画一些不同大小的圆而已,就可以实现如图的效果。

圆形头像的基本原理是将设置的资源文件转化成Bitmap,然后通过BitmapShader类将Bitmap成为Paint的渲染器,然后在onDraw()中通过canvas.drawCircle(rx,ry,radius,paint);画布上画圆,而这个圆就是形成了圆形头像。

在xml中通过class="lazy" data-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, 0, canvas.getWidth(), canvas.getHeight()); 
   drawable.setBounds(left, top, right, buttom); 
   drawable.draw(canvas); 
   return bitmap; 
  } catch (OutOfMemoryError e) { 
   return null; 
  } 
 } 
 
 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, 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,-1,-1,-1,-1,-1,-1,-1,-1, 
   0,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,-1,-1,-1,-1, 
   0,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.84f,0.80f,0.72f,0.67f,0.60f,0.54f,0.48f,-1f 
   ,-1f,-1f,-1f,-1f,-1f,-1f,-1f,-1f,-1f}; 
 //图片边框执行动画时半径变化率 
 private float mRateBorder[] = { 
   0,0.1f,0.2f,0.3f,0.4f,0.5f,0.6f,0.7f,0.8f,0.9f,1.0f 
   ,0.92f,0.90f,0.84f,0.78f,0.72f,0.64f,0.58f,-1f,-1f,-1f 
   ,-1f,-1f,-1f,-1f,-1f,-1f,-1f,-1f,-1f,-1f 
   ,-1f,-1f,-1f,-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,getHeight() / 2,getChangeRadiusInner(mInnerRadius),mInnerPaint); 
  canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint); 
  canvas.drawCircle(getWidth() / 2, getHeight() / 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; 
 
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,-1,-1,-1,-1,-1,-1,-1,-1,-1, 
   0,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,-1,-1,-1,-1, 
   0,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.84f,0.80f,0.72f,0.67f,0.60f,0.54f,0.48f,-1f 
   ,-1f,-1f,-1f,-1f,-1f,-1f,-1f,-1f,-1f}; 
 //图片边框执行动画时半径变化率 
 private float mRateBorder[] = { 
   0,0.1f,0.2f,0.3f,0.4f,0.5f,0.6f,0.7f,0.8f,0.9f,1.0f 
   ,0.92f,0.90f,0.84f,0.78f,0.72f,0.64f,0.58f,-1f,-1f,-1f 
   ,-1f,-1f,-1f,-1f,-1f,-1f,-1f,-1f,-1f,-1f 
   ,-1f,-1f,-1f,-1f}; 
 //private int mColor[] = {0x55FFFFFF,0x44FFFFFF,0x33FFFFFF,0x22FFFFFF,0x11FFFFFF,0x00FFFFFF,0x00FFFFFF,0x00FFFFFF,0x00FFFFFF,0x00FFFFFF}; 
 private int mRateIndex;//动画变化率的索引 
 // 
 //外圆执行动画时半径变化率 
 private float mRateOuterEnter[] = { 
   -2,-2,//外圆要缩小2个mChangeRange才会完全隐藏 
   -2,-2,-2,-2,-2, 
   -0.8f,-0.6f,-0.4f,-0.2f,-0.1f, 
   0,0.1f,0.2f,0.3f,0.4f,0.5f 
   ,0.44f,0.4f,0.35f,0.3f,0.25f 
   ,0.20f,0.15f,0.1f,0.0f}; 
 //内圆执行动画时半径变化率 
 private float mRateInnerEnter[] = { 
   -1,-1,//外圆要缩小1个mChangeRange才会完全隐藏 
   -0.8f,-0.6f,-0.4f,-0.2f,-0.1f, 
   0,0.1f,0.2f,0.3f,0.35f,0.4f 
   ,0.45f,0.5f,0.45f,0.4f,0.35f 
   ,0.3f,0.25f,0.2f,0.15f,0.1f 
   ,0.05f,0f,0f,0f}; 
 private int mRateIndexEnter;//进入动画变化率的索引 
 // 
 //外圆执行动画时半径变化率 
 private float mRateOuterExit[] = { 
   0.0f,-0.2f,-0.4f,-0.6f,-0.8f 
   ,-1f,-1.2f,-1.4f,-1.6f,-1.8f 
   ,-2f}; 
 //内圆执行动画时半径变化率 
 private float mRateInnerExit[] = { 
   0.0f,-0.1f,-0.2f,-0.3f,-0.4f 
   ,-0.5f,-0.6f,-0.7f,-0.8f,-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,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); 
  } 
 }; 
 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); 
  } 
 }; 
  
 private void setPaintCorlor(Paint paint,float rate,int color){ 
  if(rate < 0){ 
   paint.setColor(Color.TRANSPARENT); 
  }else{ 
   paint.setColor(color); 
  } 
 } 
  
 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, attrs, 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, top, right, bottom); 
  enterAnim(); 
 } 
 @Override 
 protected void onDraw(Canvas canvas) { 
  if (getDrawable() == null) { 
   return; 
  } 
  //画动画的图形 
  canvas.drawCircle(getWidth() / 2,getHeight() / 2,getChangeRadiusOuter(mOuterRadius),mOuterPaint); 
  canvas.drawCircle(getWidth() / 2,getHeight() / 2,getChangeRadiusInner(mInnerRadius),mInnerPaint); 
  canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint); 
  canvas.drawCircle(getWidth() / 2, getHeight() / 2, getChangeRadiusBorder(mBorderRadius), 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); 
 } 
  
 public void setOuterPaintColor(int outerPaintColor) { 
  if (outerPaintColor == mOuterPaintColor) { 
   return; 
  } 
  mOuterPaintColor = outerPaintColor; 
  mOuterPaint.setColor(mOuterPaintColor); 
  invalidate(); 
 } 
  
 public void setInnerPaintColor(int innerPaintColor) { 
  if (innerPaintColor == mInnerPaintColor) { 
   return; 
  } 
  mInnerPaintColor = innerPaintColor; 
  mInnerPaint.setColor(mInnerPaintColor); 
  invalidate(); 
 } 
  
 public void setBorderColor(int borderColor) { 
  if (borderColor == mBorderColor) { 
   return; 
  } 
  mBorderColor = borderColor; 
  mBorderPaint.setColor(mBorderColor); 
  invalidate(); 
 } 
  
 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(); 
 } 
  
 @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(); 
 } 
  
 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, cropWidth, null, 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, 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, 0, canvas.getWidth(), canvas.getHeight()); 
   drawable.setBounds(left, top, right, 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, 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自定义View实现圆环颜色选择器利用Android模仿微信摄像圆环进度效果实例Android自定义带动画的半圆环型进度效果Android自定义view绘制圆环占比动画Android自定义View实现圆环交替效果Android中自定义View实现圆环等待及相关的音量调节效果Android自定义View之酷炫圆环(二)Android自定义View之酷炫数字圆环Android开发笔记之:在ImageView上绘制圆环的实现方法Android实现光点模糊渐变的自旋转圆环特效


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android实现动态圆环的图片头像控件

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android实现动态圆环的图片头像控件

先看效果图:现在大部分的app上难免会使用到圆形头像,所以今天我给大家分享一个单独使用的,并且周围带有圆环动画的花哨圆形头像控件,本控件是在圆形头像控件基础上实现的,只是在其周围再画一些不同大小的圆而已,就可以实现如图的效果。圆形头像的基本
2022-06-06

Android实现本地上传图片并设置为圆形头像

先从本地把图片上传到服务器,然后根据URL把头像处理成圆形头像。 因为上传图片用到bmob的平台,所以要到bmob(http://www.bmob.cn)申请密钥。 效果图:核心代码:代码如下: public class MainActiv
2022-06-06

Android 自定义圆形头像CircleImageView支持加载网络图片的实现代码

在Android开发中我们常常用到圆形的头像,如果每次加载之后再进行圆形裁剪特别麻烦。所以在这里写一个自定义圆形ImageView,直接去加载网络图片,这样的话就特别的方便。 先上效果图主要的方法 1.让自定义 CircleImageVie
2022-06-06

Android实现动态定值范围效果的控件

先来看看效果:一、添加依赖库的步骤 1.项目的gradle文件内的做以下改动allprojects {repositories {...maven { url "https://jitpack.io" }}} 2.添加最新版本的依赖库,最新
2022-06-06

Android通过自定义ImageView控件实现图片的缩放和拖动的实现代码

概述:通过自定义ImageView控件,在xml布局里面调用自定的组件实现图片的缩放。 public class
2022-06-06

android Gallery组件实现的iPhone图片滑动效果实例

实现的效果图,可左右滑动:一、先在将Gallery标签放入:代码如下: 2022-06-06

android换肤功能 如何动态获取控件中背景图片的资源id?

这个是在在做一个换肤功能时遇到的问题。 对于换肤,网上都有示例,可以从别的皮肤安装包中读取所要的资源,前提是你必须先持有这个资源的引用名称,像R.drawable.background(喂,这不是废话嘛)。这个换肤的方案原理就是,自身应
2022-06-06

java spring+mybatis整合如何实现今日头条搞笑动态图片的爬取

java spring+mybatis整合如何实现今日头条搞笑动态图片的爬取,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。java spring+mybatis整合实现爬虫之今
2023-06-19

Android实现Listview异步加载网络图片并动态更新的方法

本文实例讲述了Android实现Listview异步加载网络图片并动态更新的方法。分享给大家供大家参考,具体如下: 应用实例:解析后台返回的数据,把每条都显示在ListView中,包括活动图片、店名、活动详情、地址、电话和距离等。 在布局文
2022-06-06

Android编程实现GridView控件点击图片变暗效果的方法

本文实例讲述了Android编程实现GridView控件点击图片变暗效果的方法。分享给大家供大家参考,具体如下:@Overridepublic void onCreate(Bundle savedInstanceState) { supe
2023-05-31

Android实现基于ViewPager的无限循环自动播放带指示器的轮播图CarouselFigureView控件

最近用到需要无限轮播自动播放的轮播轮播图,网上感觉都有这样那样的问题,于是自己写了一个通用的控件CarouselFigureView。 特点: 1.可以轮播view可以自己定义,不一定是要是ImageView2.指示器默认显示,但是可以隐藏
2022-06-06

Android 拍照选择图片并上传功能的实现思路(包含权限动态获取)

作为一个Android新手,想实现手机拍照并上传的功能,经过查找资料,已实现此功能。在此记录备忘。老鸟请忽略。 一、实现思路: 1.Android手机客户端,拍照(或选择图片),然后上传到服务器。 2.服务器端接收手机端上传上来的图片。 二
2022-06-06

Android实现登陆页logo随键盘收放动态伸缩(完美解决键盘弹出遮挡控件的问题)

在最近的两个项目中,项目需求要求我们实现 这样的效果,宝宝心里苦呀,本来半天搞定的事还非得折腾一下,好吧我妥协,毕竟我还是一只非常注重用户体验的猿。 那就做吧,初步
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录