Android-----自定义圆形的头像控件

亦凉 2022-08-09 09:59 325阅读 0赞

在现在的网络上圆形头像是非常常见的,圆形头像大多数使用在显示个人信息中的头像信息,今天就试试实现一个圆形的头像。

自定义一个CircleImageView,并且继承ImageView,用于显示圆形的图片。

  1. package com.gjg.circleimageviewdemo;
  2. import android.content.Context;
  3. import android.content.res.TypedArray;
  4. import android.graphics.Bitmap;
  5. import android.graphics.BitmapShader;
  6. import android.graphics.Canvas;
  7. import android.graphics.Matrix;
  8. import android.graphics.Paint;
  9. import android.graphics.RectF;
  10. import android.graphics.Shader;
  11. import android.graphics.drawable.BitmapDrawable;
  12. import android.graphics.drawable.ColorDrawable;
  13. import android.graphics.drawable.Drawable;
  14. import android.net.Uri;
  15. import android.util.AttributeSet;
  16. import android.widget.ImageView;
  17. public class CircleImageView extends ImageView{
  18. //设置scaletype为ScaleType.CENTER_CROP(均衡缩放)
  19. private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
  20. private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
  21. private static final int COLORDRAWABLE_DIMENSION = 1;
  22. private final RectF mDrawableRect = new RectF();
  23. private final RectF mBorderRect = new RectF();
  24. private final Matrix mShaderMatrix = new Matrix();
  25. private final Paint mBitmapPaint = new Paint();//画整个圆形的画笔
  26. private final Paint mBorderPaint = new Paint();//画边框的画笔
  27. private int mBorderColor;//圆边框的颜色
  28. private int mBorderWidth;//圆边框的宽度
  29. private Bitmap mBitmap;
  30. private BitmapShader mBitmapShader;
  31. private int mBitmapWidth;
  32. private int mBitmapHeight;
  33. private float mDrawableRadius;
  34. private float mBorderRadius;
  35. private boolean mReady;
  36. private boolean mSetupPending;
  37. public CircleImageView(Context context) {
  38. super(context);
  39. init();
  40. }
  41. public CircleImageView(Context context, AttributeSet attrs) {
  42. this(context, attrs, 0);
  43. init();
  44. }
  45. public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
  46. super(context, attrs, defStyle);
  47. //从attrs.xml中获取自定义的属性
  48. TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);
  49. mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, 0);
  50. mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, 0);
  51. //回收属性,避免对下次的使用造成影响
  52. a.recycle();
  53. init();
  54. }
  55. private void init() {
  56. super.setScaleType(SCALE_TYPE);
  57. mReady = true;
  58. if (mSetupPending) {
  59. setup();
  60. mSetupPending = false;
  61. }
  62. }
  63. @Override
  64. public ScaleType getScaleType() {
  65. return SCALE_TYPE;
  66. }
  67. @Override
  68. public void setScaleType(ScaleType scaleType) {
  69. if (scaleType != SCALE_TYPE) {
  70. throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
  71. }
  72. }
  73. @Override
  74. protected void onDraw(Canvas canvas) {
  75. if (getDrawable() == null) {
  76. return;
  77. }
  78. //画圆(圆形头像部分)
  79. canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint);
  80. //若设置了圆的边框宽度,将边框画出来
  81. if (mBorderWidth != 0) {
  82. canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint);
  83. }
  84. }
  85. @Override
  86. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  87. super.onSizeChanged(w, h, oldw, oldh);
  88. setup();
  89. }
  90. public int getBorderColor() {
  91. return mBorderColor;
  92. }
  93. public void setBorderColor(int borderColor) {
  94. if (borderColor == mBorderColor) {
  95. return;
  96. }
  97. mBorderColor = borderColor;
  98. mBorderPaint.setColor(mBorderColor);
  99. invalidate();
  100. }
  101. public int getBorderWidth() {
  102. return mBorderWidth;
  103. }
  104. public void setBorderWidth(int borderWidth) {
  105. if (borderWidth == mBorderWidth) {
  106. return;
  107. }
  108. mBorderWidth = borderWidth;
  109. setup();
  110. }
  111. @Override
  112. public void setImageBitmap(Bitmap bm) {
  113. super.setImageBitmap(bm);
  114. mBitmap = bm;
  115. setup();
  116. }
  117. @Override
  118. public void setImageDrawable(Drawable drawable) {
  119. super.setImageDrawable(drawable);
  120. mBitmap = getBitmapFromDrawable(drawable);
  121. setup();
  122. }
  123. @Override
  124. public void setImageResource(int resId) {
  125. super.setImageResource(resId);
  126. mBitmap = getBitmapFromDrawable(getDrawable());
  127. setup();
  128. }
  129. @Override
  130. public void setImageURI(Uri uri) {
  131. super.setImageURI(uri);
  132. mBitmap = getBitmapFromDrawable(getDrawable());
  133. setup();
  134. }
  135. /**
  136. * 获取最后的圆形图片
  137. * @param drawable
  138. * @return Bitmap
  139. */
  140. private Bitmap getBitmapFromDrawable(Drawable drawable) {
  141. if (drawable == null) {
  142. return null;
  143. }
  144. if (drawable instanceof BitmapDrawable) {
  145. return ((BitmapDrawable) drawable).getBitmap();
  146. }
  147. try {
  148. Bitmap bitmap;
  149. if (drawable instanceof ColorDrawable) {
  150. bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
  151. } else {
  152. bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
  153. }
  154. Canvas canvas = new Canvas(bitmap);
  155. drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
  156. drawable.draw(canvas);
  157. return bitmap;
  158. } catch (OutOfMemoryError e) {
  159. return null;
  160. }
  161. }
  162. private void setup() {
  163. if (!mReady) {
  164. mSetupPending = true;
  165. return;
  166. }
  167. if (mBitmap == null) {
  168. return;
  169. }
  170. mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
  171. mBitmapPaint.setAntiAlias(true);
  172. mBitmapPaint.setShader(mBitmapShader);
  173. mBorderPaint.setStyle(Paint.Style.STROKE);
  174. mBorderPaint.setAntiAlias(true);
  175. mBorderPaint.setColor(mBorderColor);
  176. mBorderPaint.setStrokeWidth(mBorderWidth);
  177. mBitmapHeight = mBitmap.getHeight();
  178. mBitmapWidth = mBitmap.getWidth();
  179. mBorderRect.set(0, 0, getWidth(), getHeight());
  180. mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2, (mBorderRect.width() - mBorderWidth) / 2);
  181. mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth);
  182. mDrawableRadius = Math.min(mDrawableRect.height() / 2, mDrawableRect.width() / 2);
  183. updateShaderMatrix();
  184. invalidate();
  185. }
  186. private void updateShaderMatrix() {
  187. float scale;
  188. float dx = 0;
  189. float dy = 0;
  190. mShaderMatrix.set(null);
  191. if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
  192. scale = mDrawableRect.height() / (float) mBitmapHeight;
  193. dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
  194. } else {
  195. scale = mDrawableRect.width() / (float) mBitmapWidth;
  196. dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
  197. }
  198. mShaderMatrix.setScale(scale, scale);
  199. mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth, (int) (dy + 0.5f) + mBorderWidth);
  200. mBitmapShader.setLocalMatrix(mShaderMatrix);
  201. }
  202. }

上面的 代码需要注意的是:

  1. public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
  2. super(context, attrs, defStyle);
  3. //从attrs.xml中获取自定义的属性
  4. TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);
  5. mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, 0);
  6. mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, 0);
  7. //回收属性,避免对下次的使用造成影响
  8. a.recycle();
  9. init();
  10. }

从attrs.xml文件中获得自定义的属性后,需要调用recycle()方法。

布局文件:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. xmlns:myapp="http://schemas.android.com/apk/res/com.gjg.circleimageviewdemo"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"
  6. android:paddingBottom="@dimen/activity_vertical_margin"
  7. android:paddingLeft="@dimen/activity_horizontal_margin"
  8. android:paddingRight="@dimen/activity_horizontal_margin"
  9. android:paddingTop="@dimen/activity_vertical_margin"
  10. tools:context=".MainActivity" >
  11. <com.gjg.circleimageviewdemo.CircleImageView
  12. android:id="@+id/head_icon2"
  13. android:layout_width="wrap_content"
  14. android:layout_height="wrap_content"
  15. android:layout_below="@+id/head_icon1"
  16. android:layout_centerHorizontal="true"
  17. android:layout_marginTop="30dp"
  18. android:background="@drawable/headicon"
  19. myapp:border_color="#328843"
  20. myapp:border_width="2dp" />
  21. <com.gjg.circleimageviewdemo.CircleImageView
  22. android:id="@+id/head_icon1"
  23. android:layout_width="wrap_content"
  24. android:layout_height="wrap_content"
  25. android:layout_alignLeft="@+id/head_icon2"
  26. android:layout_alignParentTop="true"
  27. android:layout_marginTop="81dp"
  28. android:src="@drawable/headicon"
  29. myapp:border_color="#328843"
  30. myapp:border_width="2dp" />
  31. <Button
  32. android:id="@+id/button1"
  33. android:onClick="toOtherActivity"
  34. android:layout_width="wrap_content"
  35. android:layout_height="wrap_content"
  36. android:layout_alignParentTop="true"
  37. android:layout_centerHorizontal="true"
  38. android:layout_marginTop="16dp"
  39. android:text="Button" />
  40. </RelativeLayout>

注意上面的图片引用应该是: android:src=”@drawable/headicon” 不要写成这种:android:background=”@drawable/headicon”

后面的哪种仍然是矩形显示,不能圆形显示。

布局文件中的

  1. xmlns:myapp="http://schemas.android.com/apk/res/com.gjg.circleimageviewdemo"

引用的自定义的属性,属性文件在values/attrs.xml

attrs.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <declare-styleable name="CircleImageView">
  4. <attr name="border_width" format="dimension" />
  5. <attr name="border_color" format="color" />
  6. </declare-styleable>
  7. </resources>

引用的属性的用法:

  1. myapp:border_color="#328843"
  2. myapp:border_width="2dp"

以myapp:开头,因为布局文件中的

  1. xmlns:myapp="http://schemas.android.com/apk/res/com.gjg.circleimageviewdemo"

也是定义成myapp,后面跟的直接是attrs中定义的name。

在attrs.xml中,其中resource是根标签,可以在里面定义若干个declare-styleable,中name定义了变量的名称,下面可以再自定义多个属性,针对来说,其属性的名称为”border_width”,format指定了该属性类型为dimension,只能表示字体的大小。

format还可以指定其他的类型比如;

reference 表示引用,参考某一资源ID

string 表示字符串

color 表示颜色值

dimension 表示尺寸值

boolean 表示布尔值

integer 表示整型值

float 表示浮点值

fraction 表示百分数

enum 表示枚举值

flag 表示位运算

效果如下:

Center

另外从网上搜了下,还可以用以下的方式直接将图裁剪成圆形图片,然后再显示。

  1. /**
  2. * 直接将bitmap裁剪成圆形
  3. * @param v
  4. */
  5. public void cropBitmap(View v){
  6. Bitmap src=BitmapFactory.decodeResource(getResources(), R.drawable.headicon);
  7. Bitmap bitmap=Bitmap.createBitmap(src);
  8. Bitmap newbitmap=toRoundBitmap(bitmap);
  9. ivHead.setImageBitmap(newbitmap);
  10. }
  11. /**
  12. * 转换图片成圆形
  13. *
  14. * @param bitmap 传入Bitmap对象
  15. * @return
  16. */
  17. public Bitmap toRoundBitmap(Bitmap bitmap) {
  18. int width = bitmap.getWidth();
  19. int height = bitmap.getHeight();
  20. float roundPx;
  21. float left, top, right, bottom, dst_left, dst_top, dst_right, dst_bottom;
  22. if (width <= height) {
  23. roundPx = width / 2;
  24. top = 0;
  25. bottom = width;
  26. left = 0;
  27. right = width;
  28. height = width;
  29. dst_left = 0;
  30. dst_top = 0;
  31. dst_right = width;
  32. dst_bottom = width;
  33. } else {
  34. roundPx = height / 2;
  35. float clip = (width - height) / 2;
  36. left = clip;
  37. right = width - clip;
  38. top = 0;
  39. bottom = height;
  40. width = height;
  41. dst_left = 0;
  42. dst_top = 0;
  43. dst_right = height;
  44. dst_bottom = height;
  45. }
  46. Bitmap output = Bitmap.createBitmap(width, height, Config.ARGB_8888);
  47. Canvas canvas = new Canvas(output);
  48. final int color = 0xff424242;
  49. final Paint paint = new Paint();
  50. final Rect src = new Rect((int) left, (int) top, (int) right,
  51. (int) bottom);
  52. final Rect dst = new Rect((int) dst_left, (int) dst_top,
  53. (int) dst_right, (int) dst_bottom);
  54. final RectF rectF = new RectF(dst);
  55. paint.setAntiAlias(true);
  56. canvas.drawARGB(0, 0, 0, 0);
  57. paint.setColor(color);
  58. canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
  59. paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
  60. canvas.drawBitmap(bitmap, src, dst, paint);
  61. return output;
  62. }

点击“直接裁剪”按钮,效果如下:

Center 1

demo下载地址:http://download.csdn.net/detail/dangnianmingyue_gg/9203149

发表评论

表情:
评论列表 (有 0 条评论,325人围观)

还没有评论,来说两句吧...

相关阅读

    相关 android 定义

    Android自定义View实现很简单 继承View,重写构造函数、onDraw,(onMeasure)等函数。 如果自定义的View需要有自定义的属性,需要在values