300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Android中快速自定义圆形ImageView图形!

Android中快速自定义圆形ImageView图形!

时间:2022-03-01 09:19:09

相关推荐

Android中快速自定义圆形ImageView图形!

一、问题在哪里?

问题来源于app开发中一个很常见的场景——用户头像要展示成圆的:

二、怎么搞?

机智的我,第一想法就是,切一张中间圆形透明、四周与底色相同、尺寸与头像相同的蒙板图片,盖在头像上不就完事了嘛,哈哈哈!

在背景纯色的前提下,这的确能简单解决问题,但是如果背景没有这么简单呢?

在这种不规则背景下,有两个问题:

1)背景图常常是适应手机宽度缩放,而头像的尺寸又是固定宽高DP的,所以固定的蒙板图片是没法保证在不同机型上都和背景图案吻合的。

2)在这种非纯色背景下,哪天想调整一下头像位置就得重新换图片蒙板,实在是太难维护了……

所以呢,既然头像图片肯定是方的,那就就让ImageView圆起来吧。

[转载请保留本文地址:/snser/p/5159126.html]

三、开始干活

基本思路是,自定义一个ImageView,通过重写onDraw方法画出一个圆形的图片来:

1 public class ImageViewPlus extends ImageView{ 2private Paint mPaintBitmap = new Paint(Paint.ANTI_ALIAS_FLAG); 3private Bitmap mRawBitmap; 4private BitmapShader mShader; 5private Matrix mMatrix = new Matrix(); 67public ImageViewPlus(Context context, AttributeSet attrs) { 8 super(context, attrs); 9}1011@Override12protected void onDraw(Canvas canvas) {13 Bitmap rawBitmap = getBitmap(getDrawable());14 if (rawBitmap != null){15 int viewWidth = getWidth();16 int viewHeight = getHeight();17 int viewMinSize = Math.min(viewWidth, viewHeight);18 float dstWidth = viewMinSize;19 float dstHeight = viewMinSize;20 if (mShader == null || !rawBitmap.equals(mRawBitmap)){21 mRawBitmap = rawBitmap;22 mShader = new BitmapShader(mRawBitmap, TileMode.CLAMP, TileMode.CLAMP);23 }24 if (mShader != null){25 mMatrix.setScale(dstWidth / rawBitmap.getWidth(), dstHeight / rawBitmap.getHeight());26 mShader.setLocalMatrix(mMatrix);27 }28 mPaintBitmap.setShader(mShader);29 float radius = viewMinSize / 2.0f;30 canvas.drawCircle(radius, radius, radius, mPaintBitmap);31 } else {32 super.onDraw(canvas);33 }34}35 36private Bitmap getBitmap(Drawable drawable){37 if (drawable instanceof BitmapDrawable){38 return ((BitmapDrawable)drawable).getBitmap();39 } else if (drawable instanceof ColorDrawable){40 Rect rect = drawable.getBounds();41 int width = rect.right - rect.left;42 int height = rect.bottom - rect.top;43 int color = ((ColorDrawable)drawable).getColor();44 Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);45 Canvas canvas = new Canvas(bitmap);46 canvas.drawARGB(Color.alpha(color), Color.red(color), Color.green(color), Color.blue(color));47 return bitmap;48 } else {49 return null;50 }51}52 }

分析一下代码:

canvas.drawCircle决定了画出来的形状是圆形,而圆形的内容则是通过mPaintBitmap.setShader搞定的。

其中,BitmapShader需要设置Bitmap填充ImageView的方式(CLAMP:拉伸边缘,MIRROR:镜像,REPEAT:整图重复)。

这里其实设成什么不重要,因为我们实际需要的是将Bitmap按比例缩放成跟ImageView一样大,而不是预置的三种效果。

所以,别忘了mMatrix.setScale和mShader.setLocalMatrix一起用,将图片缩放一下。

[转载请保留本文地址:/snser/p/5159126.html]

四、更多玩法 —— 支持边框

看下面的效果图,如果想给圆形的头像上加一个边框,该怎么搞呢?

1 public class ImageViewPlus extends ImageView{ 2private Paint mPaintBitmap = new Paint(Paint.ANTI_ALIAS_FLAG); 3private Paint mPaintBorder = new Paint(Paint.ANTI_ALIAS_FLAG); 4private Bitmap mRawBitmap; 5private BitmapShader mShader; 6private Matrix mMatrix = new Matrix(); 7private float mBorderWidth = dip2px(15); 8private int mBorderColor = 0xFF0080FF; 910public ImageViewPlus(Context context, AttributeSet attrs) {11 super(context, attrs);12}1314@Override15protected void onDraw(Canvas canvas) {16 Bitmap rawBitmap = getBitmap(getDrawable());17 if (rawBitmap != null){18 int viewWidth = getWidth();19 int viewHeight = getHeight();20 int viewMinSize = Math.min(viewWidth, viewHeight);21 float dstWidth = viewMinSize;22 float dstHeight = viewMinSize;23 if (mShader == null || !rawBitmap.equals(mRawBitmap)){24 mRawBitmap = rawBitmap;25 mShader = new BitmapShader(mRawBitmap, TileMode.CLAMP, TileMode.CLAMP);26 }27 if (mShader != null){28 mMatrix.setScale((dstWidth - mBorderWidth * 2) / rawBitmap.getWidth(), (dstHeight - mBorderWidth * 2) / rawBitmap.getHeight());29 mShader.setLocalMatrix(mMatrix);30 }31 mPaintBitmap.setShader(mShader);32 mPaintBorder.setStyle(Paint.Style.STROKE);33 mPaintBorder.setStrokeWidth(mBorderWidth);34 mPaintBorder.setColor(mBorderColor);35 float radius = viewMinSize / 2.0f;36 canvas.drawCircle(radius, radius, radius - mBorderWidth / 2.0f, mPaintBorder);37 canvas.translate(mBorderWidth, mBorderWidth);38 canvas.drawCircle(radius - mBorderWidth, radius - mBorderWidth, radius - mBorderWidth, mPaintBitmap);39 } else {40 super.onDraw(canvas);41 }42}43 44private Bitmap getBitmap(Drawable drawable){45 if (drawable instanceof BitmapDrawable){46 return ((BitmapDrawable)drawable).getBitmap();47 } else if (drawable instanceof ColorDrawable){48 Rect rect = drawable.getBounds();49 int width = rect.right - rect.left;50 int height = rect.bottom - rect.top;51 int color = ((ColorDrawable)drawable).getColor();52 Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);53 Canvas canvas = new Canvas(bitmap);54 canvas.drawARGB(Color.alpha(color), Color.red(color), Color.green(color), Color.blue(color));55 return bitmap;56 } else {57 return null;58 }59}6061private int dip2px(int dipVal)62{63 float scale = getResources().getDisplayMetrics().density;64 return (int)(dipVal * scale + 0.5f);65}66 }

看代码中,加边框实际上就是用实心纯色的Paint画了一个圆边,在此基础上画上原来的头像即可。

需要的注意的地方有三个:

1)圆框的半径不是radius,而应该是radius - mBorderWidth / 2.0f。想象着拿着笔去画线,线其实是画在右图中白色圈的位置,只不过它很粗。

2)在ImageView大小不变的基础上,头像的实际大小要比没有边框的时候小了,所以mMatrix.setScale的时候要把边框的宽度去掉。

3)画头像Bitmap的时候不能直接canvas.drawCircle(radius, radius, radius - mBorderWidth, mPaintBitmap),这样你会发现头像的右侧和下方边缘被拉伸了(右图)

为什么呢?因为Paint默认是以左上角为基准开始绘制的,此时头像的实际区域是右图中的红框,而超过红框的部分(圆形的右侧和下方),自然被TileMode.CLAMP效果沿边缘拉伸了。

所以,需要通过挪动坐标系的位置和调整圆心,才能把头像画在正确的区域(右图绿框)中。

[转载请保留本文地址:/snser/p/5159126.html]

五、更多玩法——支持xml配置

既然有了边框,那如果想配置边框的宽度和颜色该如何是好呢?

基本上两个思路:

1)给ImageViewPlus加上set接口,设置完成之后通过invalidate();重绘一下即可;

2)在xml里就支持配置一些自定义属性,这样用起来会方便很多。

这里重点说一下支持xml配置自定义属性。

自定义控件要支持xml配置自定义属性的话,首先需要在\res\values里去定义属性:

1 <?xml version="1.0" encoding="utf-8"?> 2 <resources> 3<attr name="borderColor" format="color" /> 4<attr name="borderWidth" format="dimension" /> 5 6<declare-styleable name="ImageViewPlus"> 7 <attr name="borderColor" /> 8 <attr name="borderWidth" /> 9</declare-styleable> 10 </resources>

然后在ImageViewPlus的构造函数中去读取这些自定义属性:

1private static final int DEFAULT_BORDER_COLOR = Color.TRANSPARENT; 2private static final int DEFAULT_BORDER_WIDTH = 0; 34public ImageViewPlus(Context context, AttributeSet attrs) { 5 super(context, attrs); 6 //取xml文件中设定的参数 7 TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ImageViewPlus); 8 mBorderColor = ta.getColor(R.styleable.ImageViewPlus_borderColor, DEFAULT_BORDER_COLOR); 9 mBorderWidth = ta.getDimensionPixelSize(R.styleable.ImageViewPlus_borderWidth, dip2px(DEFAULT_BORDER_WIDTH));10 ta.recycle();11}

在xml布局中使用自定义属性:

1 <RelativeLayout xmlns:android="/apk/res/android" 2xmlns:tools="/tools" 3xmlns:snser="/apk/res/cc.snser.imageviewplus" 4android:layout_width="match_parent" 5android:layout_height="match_parent" 6android:background="@drawable/wallpaper" 7android:orientation="vertical" 8tools:context="${relativePackage}.${activityClass}" > 910<cc.snser.imageviewplus.ImageViewPlus11 android:id="@+id/imgplus"12 android:layout_width="200dp"13 android:layout_height="300dp"14 android:layout_marginBottom="50dp"15 android:layout_centerHorizontal="true"16 android:layout_alignParentBottom="true"17 android:src="@drawable/img_square"18 snser:borderColor="#FF0080FF"19 snser:borderWidth="15dp" /> </RelativeLayout>

[转载请保留本文地址:/snser/p/5159126.html]

六、更多玩法——圆角ImageView

搞定了圆形ImageView以及对应的边框,那如何实现下面这种圆角的ImageView呢?

其实原理上一样,把canvas.drawCircle对应改成canvas.drawRoundRect就OK了,直接贴代码吧:

1 public class ImageViewPlus extends ImageView{ 2/** 3* android.widget.ImageView 4*/ 5public static final int TYPE_NONE = 0; 6/** 7* 圆形 8*/ 9public static final int TYPE_CIRCLE = 1; 10/** 11* 圆角矩形 12*/ 13public static final int TYPE_ROUNDED_RECT = 2;1415private static final int DEFAULT_TYPE = TYPE_NONE; 16private static final int DEFAULT_BORDER_COLOR = Color.TRANSPARENT; 17private static final int DEFAULT_BORDER_WIDTH = 0; 18private static final int DEFAULT_RECT_ROUND_RADIUS = 0; 1920private int mType; 21private int mBorderColor; 22private int mBorderWidth; 23private int mRectRoundRadius; 2425private Paint mPaintBitmap = new Paint(Paint.ANTI_ALIAS_FLAG); 26private Paint mPaintBorder = new Paint(Paint.ANTI_ALIAS_FLAG); 2728private RectF mRectBorder = new RectF(); 29private RectF mRectBitmap = new RectF(); 3031private Bitmap mRawBitmap; 32private BitmapShader mShader; 33private Matrix mMatrix = new Matrix(); 3435public ImageViewPlus(Context context, AttributeSet attrs) { 36 super(context, attrs); 37 //取xml文件中设定的参数 38 TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ImageViewPlus); 39 mType = ta.getInt(R.styleable.ImageViewPlus_type, DEFAULT_TYPE); 40 mBorderColor = ta.getColor(R.styleable.ImageViewPlus_borderColor, DEFAULT_BORDER_COLOR); 41 mBorderWidth = ta.getDimensionPixelSize(R.styleable.ImageViewPlus_borderWidth, dip2px(DEFAULT_BORDER_WIDTH)); 42 mRectRoundRadius = ta.getDimensionPixelSize(R.styleable.ImageViewPlus_rectRoundRadius, dip2px(DEFAULT_RECT_ROUND_RADIUS)); 43 ta.recycle(); 44} 4546@Override 47protected void onDraw(Canvas canvas) { 48 Bitmap rawBitmap = getBitmap(getDrawable()); 4950 if (rawBitmap != null && mType != TYPE_NONE){ 51 int viewWidth = getWidth(); 52 int viewHeight = getHeight(); 53 int viewMinSize = Math.min(viewWidth, viewHeight); 54 float dstWidth = mType == TYPE_CIRCLE ? viewMinSize : viewWidth; 55 float dstHeight = mType == TYPE_CIRCLE ? viewMinSize : viewHeight; 56 float halfBorderWidth = mBorderWidth / 2.0f; 57 float doubleBorderWidth = mBorderWidth * 2; 58 59 if (mShader == null || !rawBitmap.equals(mRawBitmap)){ 60 mRawBitmap = rawBitmap; 61 mShader = new BitmapShader(mRawBitmap, TileMode.CLAMP, TileMode.CLAMP); 62 } 63 if (mShader != null){ 64 mMatrix.setScale((dstWidth - doubleBorderWidth) / rawBitmap.getWidth(), (dstHeight - doubleBorderWidth) / rawBitmap.getHeight()); 65 mShader.setLocalMatrix(mMatrix); 66 } 67 68 mPaintBitmap.setShader(mShader); 69 mPaintBorder.setStyle(Paint.Style.STROKE); 70 mPaintBorder.setStrokeWidth(mBorderWidth); 71 mPaintBorder.setColor(mBorderWidth > 0 ? mBorderColor : Color.TRANSPARENT); 72 73 if (mType == TYPE_CIRCLE){ 74 float radius = viewMinSize / 2.0f; 75 canvas.drawCircle(radius, radius, radius - halfBorderWidth, mPaintBorder); 76 canvas.translate(mBorderWidth, mBorderWidth); 77 canvas.drawCircle(radius - mBorderWidth, radius - mBorderWidth, radius - mBorderWidth, mPaintBitmap); 78 } else if (mType == TYPE_ROUNDED_RECT){ 79 mRectBorder.set(halfBorderWidth, halfBorderWidth, dstWidth - halfBorderWidth, dstHeight - halfBorderWidth); 80 mRectBitmap.set(0.0f, 0.0f, dstWidth - doubleBorderWidth, dstHeight - doubleBorderWidth); 81 float borderRadius = mRectRoundRadius - halfBorderWidth > 0.0f ? mRectRoundRadius - halfBorderWidth : 0.0f; 82 float bitmapRadius = mRectRoundRadius - mBorderWidth > 0.0f ? mRectRoundRadius - mBorderWidth : 0.0f; 83 canvas.drawRoundRect(mRectBorder, borderRadius, borderRadius, mPaintBorder); 84 canvas.translate(mBorderWidth, mBorderWidth); 85 canvas.drawRoundRect(mRectBitmap, bitmapRadius, bitmapRadius, mPaintBitmap); 86 } 87 } else { 88 super.onDraw(canvas); 89 } 90} 91 92private int dip2px(int dipVal) 93{ 94 float scale = getResources().getDisplayMetrics().density; 95 return (int)(dipVal * scale + 0.5f); 96} 9798private Bitmap getBitmap(Drawable drawable){ 99 if (drawable instanceof BitmapDrawable){100 return ((BitmapDrawable)drawable).getBitmap();101 } else if (drawable instanceof ColorDrawable){102 Rect rect = drawable.getBounds();103 int width = rect.right - rect.left;104 int height = rect.bottom - rect.top;105 int color = ((ColorDrawable)drawable).getColor();106 Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);107 Canvas canvas = new Canvas(bitmap);108 canvas.drawARGB(Color.alpha(color), Color.red(color), Color.green(color), Color.blue(color));109 return bitmap;110 } else {111 return null;112 }113}114 }

1 <RelativeLayout xmlns:android="/apk/res/android" 2xmlns:tools="/tools" 3xmlns:snser="/apk/res/cc.snser.imageviewplus" 4android:layout_width="match_parent" 5android:layout_height="match_parent" 6android:background="@drawable/wallpaper" 7android:orientation="vertical" 8tools:context="${relativePackage}.${activityClass}" > 910<cc.snser.imageviewplus.ImageViewPlus11 android:id="@+id/imgplus"12 android:layout_width="200dp"13 android:layout_height="300dp"14 android:layout_marginBottom="50dp"15 android:layout_centerHorizontal="true"16 android:layout_alignParentBottom="true"17 android:src="@drawable/img_rectangle"18 snser:type="rounded_rect"19 snser:borderColor="#FF0080FF"20 snser:borderWidth="10dp"21 snser:rectRoundRadius="30dp" />2223 </RelativeLayout>

1 <?xml version="1.0" encoding="utf-8"?> 2 <resources> 3<attr name="type"> 4 <enum name="none" value="0" /> 5 <enum name="circle" value="1" /> 6 <enum name="rounded_rect" value="2" /> 7</attr> 8<attr name="borderColor" format="color" /> 9<attr name="borderWidth" format="dimension" />10<attr name="rectRoundRadius" format="dimension" />11 12<declare-styleable name="ImageViewPlus"> 13 <attr name="type" />14 <attr name="borderColor" />15 <attr name="borderWidth" />16 <attr name="rectRoundRadius" />17</declare-styleable>18 </resources>

[转载请保留本文地址:/snser/p/5159126.html]

七、Demo源码

保存下面的图片,扩展名改成zip后解压即可。

[转载自:/snser/p/5159126.html]

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。