android 如何重写imageview 让图片有圆角效果
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android 如何重写imageview 让图片有圆角效果相关的知识,希望对你有一定的参考价值。
android 自定义圆角ImageView以及锯齿的处理
看到很多人开发过程中要使用圆角图片时,解决方法有:
1.重新绘制一张图片
2.通过布局来配置
3.通过重写View来实现
其中1,2在这里就不讲了,重点讲讲方法三的实现。
实现一:通过截取画布一个圆形区域与图片的相交部分进行绘制,缺点:锯齿明显,设置Paint,Canvas抗锯齿无效。
package com.open.circleimageview.widget;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.AttributeSet;
import android.view.View;
public class CircleImageViewA extends View
public CircleImageViewA(Context context, AttributeSet attrs, int defStyle)
super(context, attrs, defStyle);
public CircleImageViewA(Context context, AttributeSet attrs)
super(context, attrs);
public CircleImageViewA(Context context)
super(context);
private Bitmap bitmap;
private Rect bitmapRect=new Rect();
private PaintFlagsDrawFilter pdf=new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG);
private Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
paint.setAntiAlias(true);// 设置画笔的锯齿效果。 true是去除,大家一看效果就明白了
private Path mPath=new Path();
public void setImageBitmap(Bitmap bitmap)
this.bitmap=bitmap;
@Override
protected void onDraw(Canvas canvas)
if(null==bitmap)
return;
bitmapRect.set(0, 0, getWidth(), getHeight());
canvas.save();
canvas.setDrawFilter(pdf);
mPath.reset();
canvas.clipPath(mPath); // makes the clip empty
mPath.addCircle(getWidth()/2, getWidth()/2, getHeight()/2, Path.Direction.CCW);
canvas.clipPath(mPath, Region.Op.REPLACE);
canvas.drawBitmap(bitmap, null, bitmapRect, paint);
canvas.restore();
实现二:通过PorterDuffXfermode 方式(注意要设置硬件加速,否则部分机子无效),优点:锯齿基本没有
package com.open.circleimageview.widget;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
public class CircleImageViewB extends View
public CircleImageViewB(Context context, AttributeSet attrs, int defStyle)
super(context, attrs, defStyle);
init();
public CircleImageViewB(Context context, AttributeSet attrs)
super(context, attrs);
init();
public CircleImageViewB(Context context)
super(context);
init();
private Bitmap bitmap;
private Rect bitmapRect=new Rect();
private PaintFlagsDrawFilter pdf=new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG);
private Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
paint.setAntiAlias(true);// 设置画笔的锯齿效果。 true是去除,大家一看效果就明白了
private Bitmap mDstB=null;
private PorterDuffXfermode xfermode=new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY);
private void init()
try
if(android.os.Build.VERSION.SDK_INT>=11)
setLayerType(LAYER_TYPE_SOFTWARE, null);
catch (Exception e)
e.printStackTrace();
public void setImageBitmap(Bitmap bitmap)
this.bitmap=bitmap;
private Bitmap makeDst(int w, int h)
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(Color.parseColor("#ffffffff"));
c.drawOval(new RectF(0, 0, w, h), p);
return bm;
@Override
protected void onDraw(Canvas canvas)
if(null==bitmap)
return;
if(null==mDstB)
mDstB=makeDst(getWidth(), getHeight());
bitmapRect.set(0, 0, getWidth(), getHeight());
canvas.save();
canvas.setDrawFilter(pdf);
canvas.drawBitmap(mDstB, 0, 0, paint);
paint.setXfermode(xfermode);
canvas.drawBitmap(bitmap, null, bitmapRect, paint);
paint.setXfermode(null);
canvas.restore();
方法1:自定义view,使用BitmapShader
首先自定义一些view的属性
下面定义了一个枚举和一个圆角的大小borderRadius。
<?xml version="1.0" encoding="utf-8"?><resources>
<attr name="borderRadius" format="dimension" />
<attr name="type">
<enum name="circle" value="0" />
<enum name="round" value="1" />
</attr>
<declare-styleable name="RoundImageView">
<attr name="borderRadius" />
<attr name="type" />
</declare-styleable>
</resources>
自定义View代码如下
public class RoundImageView extends ImageView/** 图片的类型,圆形or圆角 */
private int type;
private static final int TYPE_CIRCLE = 0;
private static final int TYPE_ROUND = 1;
/** 圆角大小的默认值 */
private static final int BODER_RADIUS_DEFAULT = 10;
/** * 圆角的大小 */
private int mBorderRadius;
/** * 绘图的Paint */
private Paint mBitmapPaint;
/** * 圆角的半径 */
private int mRadius;
/** * 3x3 矩阵,主要用于缩小放大 */
private Matrix mMatrix;
/** * 渲染图像,使用图像为绘制图形着色 */
private BitmapShader mBitmapShader;
/** * view的宽度 */
private int mWidth;
private RectF mRoundRect;
public RoundImageView(Context context, AttributeSet attrs)
super(context, attrs);
mMatrix = new Matrix();
mBitmapPaint = new Paint();
mBitmapPaint.setAntiAlias(true);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.RoundImageView);
mBorderRadius = a.getDimensionPixelSize(
R.styleable.RoundImageView_borderRadius, (int) TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
BODER_RADIUS_DEFAULT, getResources()
.getDisplayMetrics()));// 默认为10dp
type = a.getInt(R.styleable.RoundImageView_type, TYPE_CIRCLE);// 默认为Circle
a.recycle();
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (type == TYPE_CIRCLE)
mWidth = Math.min(getMeasuredWidth(), getMeasuredHeight());
mRadius = mWidth / 2;
setMeasuredDimension(mWidth, mWidth);
private void setUpShader()
Drawable drawable = getDrawable();
if (drawable == null) return;
Bitmap bmp = drawableToBitamp(drawable);
// 将bmp作为着色器,就是在指定区域内绘制bmp
mBitmapShader = new BitmapShader(bmp, TileMode.CLAMP, TileMode.CLAMP);
float scale = 1.0f;
if (type == TYPE_CIRCLE)
int bSize = Math.min(bmp.getWidth(), bmp.getHeight());
scale = mWidth * 1.0f / bSize;
else if (type == TYPE_ROUND)
scale = Math.max(getWidth() * 1.0f / bmp.getWidth(), getHeight()
* 1.0f / bmp.getHeight());
// shader的变换矩阵,我们这里主要用于放大或者缩小
mMatrix.setScale(scale, scale);
// 设置变换矩阵
mBitmapShader.setLocalMatrix(mMatrix);
// 设置shader
mBitmapPaint.setShader(mBitmapShader);
private Bitmap drawableToBitamp(Drawable drawable)
if (drawable instanceof BitmapDrawable)
BitmapDrawable bd = (BitmapDrawable) drawable;
return bd.getBitmap();
int w = drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight();
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, w, h);
drawable.draw(canvas);
return bitmap;
@Override
protected void onDraw(Canvas canvas)
if (getDrawable() == null) return;
setUpShader();
if (type == TYPE_ROUND)
canvas.drawRoundRect(mRoundRect, mBorderRadius, mBorderRadius,
mBitmapPaint);
else
canvas.drawCircle(mRadius, mRadius, mRadius, mBitmapPaint);
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
super.onSizeChanged(w, h, oldw, oldh);
// 圆角图片的范围
if (type == TYPE_ROUND)
mRoundRect = new RectF(0, 0, getWidth(), getHeight());
方法2:自定义view,使用Xfermode
依然是自定义属性
<?xml version="1.0" encoding="utf-8"?><resources>
<attr name="borderRadius" format="dimension" />
<attr name="type">
<enum name="circle" value="0" />
<enum name="round" value="1" />
</attr>
<declare-styleable name="RoundImageViewByXfermode">
<attr name="borderRadius" />
<attr name="type" />
</declare-styleable>
</resources>
代码:
private Paint mPaint;
private Xfermode mXfermode = new PorterDuffXfermode(Mode.DST_IN);
private Bitmap mMaskBitmap;
private WeakReference<Bitmap> mWeakBitmap;
private int type;
public static final int TYPE_CIRCLE = 0;
public static final int TYPE_ROUND = 1;
/** * 圆角大小的默认值 */
private static final int BODER_RADIUS_DEFAULT = 10;
private int mBorderRadius;
public RoundImageViewByXfermode(Context context)
this(context,null);
mPaint = new Paint();
mPaint.setAntiAlias(true);
public RoundImageViewByXfermode(Context context, AttributeSet attrs)
super(context, attrs);
mPaint = new Paint();
mPaint.setAntiAlias(true);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.RoundImageViewByXfermode);
mBorderRadius = a.getDimensionPixelSize(
R.styleable.RoundImageViewByXfermode_borderRadius, (int) TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
BODER_RADIUS_DEFAULT, getResources()
.getDisplayMetrics()));// 默认为10dp
Log.e("TAG", mBorderRadius+"");
type = a.getInt(R.styleable.RoundImageViewByXfermode_type, TYPE_CIRCLE);// 默认为Circle
a.recycle();
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/** 果类型是圆形,则强制改变view的宽高一致,以小值为准*/
if (type == TYPE_CIRCLE)
int width = Math.min(getMeasuredWidth(), getMeasuredHeight());
setMeasuredDimension(width, width);
@Override
protected void onDraw(Canvas canvas)
Bitmap bitmap = mWeakBitmap == null ? null : mWeakBitmap.get();
if (null == bitmap || bitmap.isRecycled())
Drawable drawable = getDrawable();
int dWidth = drawable.getIntrinsicWidth();
int dHeight = drawable.getIntrinsicHeight();
if (drawable != null)
//创建bitmap
bitmap = Bitmap.createBitmap(getWidth(), getHeight(),
Config.ARGB_8888);
float scale = 1.0f;
Canvas drawCanvas = new Canvas(bitmap);
//按照bitmap的宽高,以及view的宽高,计算缩放比例;因为设置的src宽高比例可能和imageview的宽高比例不同,这里我们不希望图片失真;
if (type == TYPE_ROUND)
// 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值;
scale = Math.max(getWidth() * 1.0f / dWidth, getHeight()
* 1.0f / dHeight);
else
scale = getWidth() * 1.0F / Math.min(dWidth, dHeight);
//根据缩放比例,设置bounds,相当于缩放图片了
drawable.setBounds(0, 0, (int) (scale * dWidth),
(int) (scale * dHeight));
drawable.draw(drawCanvas);
if (mMaskBitmap == null || mMaskBitmap.isRecycled())
mMaskBitmap = getBitmap();
// Draw Bitmap.
mPaint.reset();
mPaint.setFilterBitmap(false);
mPaint.setXfermode(mXfermode);
//绘制形状
drawCanvas.drawBitmap(mMaskBitmap, 0, 0, mPaint);
mPaint.setXfermode(null);
//将准备好的bitmap绘制出来
canvas.drawBitmap(bitmap, 0, 0, null);
//bitmap缓存起来,避免每次调用onDraw,分配内存
mWeakBitmap = new WeakReference<Bitmap>(bitmap);
//如果bitmap还存在,则直接绘制即可
if (bitmap != null)
mPaint.setXfermode(null);
canvas.drawBitmap(bitmap, 0.0f, 0.0f, mPaint);
return;
/**
* 绘制形状
* @return
*/
public Bitmap getBitmap()
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
if (type == TYPE_ROUND)
canvas.drawRoundRect(new RectF(0, 0, getWidth(), getHeight()),
mBorderRadius, mBorderRadius, paint);
else
canvas.drawCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2,
paint);
return bitmap;
@Override
public void invalidate()
mWeakBitmap = null;
if (mMaskBitmap != null)
mMaskBitmap.recycle();
mMaskBitmap = null;
super.invalidate();
参考技术B 在drawable下建一个xml文件,如image_corner_round.xml,把下面的写进去,
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient android:startColor="#B5E7B8"
android:endColor="#76D37B"
android:angle="270"/>
<corners android:topLeftRadius="4dip"
android:topRightRadius="4dip"
android:bottomLeftRadius="4dip"
android:bottomRightRadius="4dip"/>
</shape>
然后在imageview的background属性设置为 @drawable/image_corner_round 参考技术C 给你提供一个思路吧,如果你的图片都是一样大的话,你可以做一个中间透明,四个角黑色的图片作为遮罩层,覆盖在图片上 参考技术D DisplayImageOptions option = new DisplayImageOptions.Builder().showStubImage(R.drawable.avatar_default).displayer(new RoundedBitmapDisplayer(4)).cacheOnDisc(true).build();
以上是关于android 如何重写imageview 让图片有圆角效果的主要内容,如果未能解决你的问题,请参考以下文章
android如何实现消除imageview周围的一圈细细的白边?
android 如何让imageview一直在最顶层,小尺寸图片,像淘宝客户端那种工具栏按钮
Android中ImnageView 中如何让图片拉伸显示?