Android实现自定义圆形/圆角ImageView

时间:2023-02-12 20:34:59

在进行开发的时候有时候会有对圆形ImageView的需求,然后当我搜索网上现有博客的方法去实现的时候发现实现的效果总是不尽如人意:有的无法实现图片的缩放;有的Xfermode的DST_IN无效,无法实现效果(具体原因并没有搞清楚,好像是new canvas的时候必须将bitmap传入)。于是对网上的例子进行了一定修改,有不足的地方还望指正。

该ImageView可实现圆角、圆形图片显示,并可以自定义圆角半径。首先我们来看一下实现效果:

Android实现自定义圆形/圆角ImageView

Android实现自定义圆形/圆角ImageView


首先介绍一下Xfermode的几种图片合成方式:

1.PorterDuff.Mode.CLEAR

所绘制不会提交到画布上。
2.PorterDuff.Mode.SRC

显示上层绘制图片
3.PorterDuff.Mode.DST

显示下层绘制图片
4.PorterDuff.Mode.SRC_OVER

正常绘制显示,上下层绘制叠盖。
5.PorterDuff.Mode.DST_OVER

上下层都显示。下层居上显示。
6.PorterDuff.Mode.SRC_IN

取两层绘制交集。显示上层。
7.PorterDuff.Mode.DST_IN

取两层绘制交集。显示下层。
8.PorterDuff.Mode.SRC_OUT

取上层绘制非交集部分。
9.PorterDuff.Mode.DST_OUT

取下层绘制非交集部分。
10.PorterDuff.Mode.SRC_ATOP

取下层非交集部分与上层交集部分
11.PorterDuff.Mode.DST_ATOP

取上层非交集部分与下层交集部分
12.PorterDuff.Mode.XOR
13.PorterDuff.Mode.DARKEN
14.PorterDuff.Mode.LIGHTEN
15.PorterDuff.Mode.MULTIPLY
16.PorterDuff.Mode.SCREEN

Android实现自定义圆形/圆角ImageView


这里我们将要用到的是SRC_IN,将会在后面的代码中见到它。


接下来看一下工程目录结构:

Android实现自定义圆形/圆角ImageView

CustomImageView实现:

首先我们先自定义两个参数imageType和radius,imageType是个枚举类型:circle、round、normal,分别实现圆形、圆角和正常图片的显示,并在Java文件中定义好。

<resources>
<attr name="imageType">
<enum name="normal" value="0" />
<enum name="round" value="1" />
<enum name="circle" value="2" />
</attr>

<declare-styleable name="CustomImageView">
<attr name="imageType" />
<attr name="radius" format="dimension" />
</declare-styleable>
</resources>
public static final int NORMAL = 0;
public static final int ROUNDREC = 1;
public static final int CIRCLE = 2;

接下来我们在控件构造的时候读取出imageType和radius,同时进行一些必要的初始化操作:

final TypedArray a = getContext().obtainStyledAttributes(
attrs, R.styleable.CustomImageView, defStyle, 0);


if (a.hasValue(R.styleable.CustomImageView_imageType)) {
imageType = a.getInt(
R.styleable.CustomImageView_imageType, 0);
}

mRadius = a.getDimension(R.styleable.CustomImageView_radius, 20);

if (null != getDrawable()) {
mSrc = (((BitmapDrawable) getDrawable()).getBitmap());
}

a.recycle();

mPaint = new Paint();
mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.BLACK);
mPaint.setTextSize(80);

invalidate();

因为实现圆形需要保证imageview是正方形的,所以我们要重写onmeasure()方法:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int mode = MeasureSpec.getMode(widthMeasureSpec);
int size = MeasureSpec.getSize(widthMeasureSpec);
if (mode == MeasureSpec.EXACTLY) {
mWidth = size;
} else {
mWidth = getPaddingLeft() + getPaddingRight() + mSrc.getWidth();
if (mode == MeasureSpec.AT_MOST) {
mWidth = Math.min(mWidth, size);
}
}

mode = MeasureSpec.getMode(heightMeasureSpec);
size = MeasureSpec.getSize(heightMeasureSpec);
if (mode == MeasureSpec.EXACTLY) {
mHeight = size;
} else {
mHeight = getPaddingTop() + getPaddingBottom() + mSrc.getHeight();
if (mode == MeasureSpec.AT_MOST) {
mHeight = Math.min(mHeight, size);
}
}
if (CIRCLE == imageType) {
int min = Math.min(mWidth, mHeight);
setMeasuredDimension(min, min);
} else
setMeasuredDimension(mWidth, mHeight);
}

最后是图形的绘制:

@Override
protected void onDraw(Canvas canvas) {

int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
int paddingRight = getPaddingRight();
int paddingBottom = getPaddingBottom();

int contentWidth = getWidth() - paddingLeft - paddingRight;
int contentHeight = getHeight() - paddingTop - paddingBottom;

RectF ovalRect = new RectF(paddingLeft, paddingTop, contentWidth, contentHeight);

switch (imageType) {
case NORMAL:
canvas.drawBitmap(mSrc, new Rect(0, 0, mSrc.getWidth(), mSrc.getHeight()), ovalRect, mPaint);
break;
case ROUNDREC:
canvas.drawBitmap(tranformBitmap(mSrc,ROUNDREC,mRadius), new Rect(0, 0, mSrc.getWidth(), mSrc.getHeight()), ovalRect, mPaint);
break;
case CIRCLE:
canvas.drawBitmap(tranformBitmap(mSrc,CIRCLE,0), new Rect(0, 0, mSrc.getWidth(), mSrc.getHeight()), ovalRect, mPaint);
break;
}

}

这其中有个特别重要的方法是

tranformBitmap(Bitmap bitmap, int imageType, float mRadius)

这个方法将bitmap转成圆形或圆角bitmap:

public static Bitmap tranformBitmap(Bitmap bitmap, int imageType, float mRadius) {

Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);

Canvas canvas = new Canvas(output);


final Paint paint = new Paint();

final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());

final RectF rectF = new RectF(rect);


paint.setAntiAlias(true);


if (imageType == CIRCLE) {
canvas.drawOval(rectF, paint);
} else {
canvas.drawRoundRect(rectF,mRadius,mRadius,paint);
}

paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

canvas.drawBitmap(bitmap, rect, rect, paint);
return output;

}

demo的下载地址: http://download.csdn.net/detail/piyell/9459709