在Android开发的过程中,我们常常需要显示不同形状的图片(圆角、或圆形等),而网上的很多例子都是直接使用集成View然后重写onDraw的方法去实现,这样的方式固然简单直接,但这样做的话会失去很多重要实用的ImageView特性(例如:scaletype)。因此,本文旨在举出下面例子,展示自定义圆角ImageView的基本方法和原理。
XMl布局代码:
<span style="font-family:Microsoft YaHei;font-size:14px;"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
android:orientation="vertical" >
<ImageView
android:id="@+id/imageView"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center"
android:src="@drawable/bg_00"/>
<com.example.component.ShapeImageView
android:id="@+id/shapeImageView"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center"
android:src="@drawable/bg_00"/>
</LinearLayout>
</span>
Java代码:
<span style="font-family:Microsoft YaHei;font-size:14px;">public class ShapeImageView extends ImageView {
public static final String TAG = "ShapeImageView";
public static final int RECTANGLE= 0;
public static final int ROUND_RECTANGLE = 1;
public static final int CIRCLE = 2;
/**圆角矩形的四角弧度,单位是像素*/
private static final int RADIUS_X = 20;
private static final int RADIUS_Y = 20;
private Bitmap mShapeBmp=null;
private BitmapDrawable mBmpDrawable = null;
private int mShape = ROUND_RECTANGLE;
private int mRadiusX = RADIUS_X;
private int mRadiusY = RADIUS_Y;
private int mWidth;
private int mHeight;
//paint的遮罩层,canvas在绘制原图和下一张图时,两者的交互绘制模式(相交、合并、异或等)
private PorterDuffXfermode xfermode=new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
//Paint的标识绘制过滤器,用于设置图片绘制的抗锯齿效果
private PaintFlagsDrawFilter pdf=new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG);
public ShapeImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ShapeImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ShapeImageView(Context context) {
super(context);
}
public int getShape() {
return mShape;
}
public void setShape(int mShape) {
this.mShape = mShape;
}
public int getRadiusX() {
if(mShape == ROUND_RECTANGLE)
return mRadiusX;
else
return 0;
}
public int getRadiusY() {
if(mShape == ROUND_RECTANGLE)
return mRadiusY;
else
return 0;
}
/**
* 设置圆角矩形的弧度(当shape=ROUND_TANGLE才有效)
* @param mRadiusX
*/
public void setRadius(int radiusX, int radiusY) {
if(mShape == ROUND_RECTANGLE){
this.mRadiusX = radiusX;
this.mRadiusY = radiusY;
}
}
/**
* 生成几何蒙版bitmap,即创建图片想要的几何形状(圆角、圆形等)的底板,用于与原图相交绘制
* @param w
* @param h
* @return
*/
private Bitmap getShapeBitmap(int width, int height, Bitmap bitmapSrc, int shape)
{
float widthScaleRatio, heightScaleRatio;//缩放比例,用于缩放圆角,使显示的圆角总是XY半径相等
int bmpSrcWidth = bitmapSrc.getWidth();
int bmpSrcHeight = bitmapSrc.getHeight();
Bitmap bmpDst = Bitmap.createBitmap(bmpSrcWidth, bmpSrcHeight, Config.ARGB_8888);//创建蒙板bitmap
Canvas canvas = new Canvas(bmpDst);//创建蒙板画布
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);//创建透明抗锯齿画笔
p.setColor(Color.parseColor("#ffffffff"));
widthScaleRatio = (1.0f*bmpSrcWidth)/(1.0f*width);//原图与目标bounds的缩放比例
heightScaleRatio = (1.0f*bmpSrcHeight)/(1.0f*height);
canvas.setDrawFilter(pdf);
//根据shape值绘制不同的几何蒙版
switch (shape) {
case ROUND_RECTANGLE:
canvas.drawRoundRect(new RectF(0,0,bmpSrcWidth,bmpSrcHeight),
mRadiusX*widthScaleRatio, mRadiusY*heightScaleRatio, p);//绘制蒙版bitmap(PS:圆角的半径的单位是像素)
break;
case CIRCLE:
canvas.drawOval(new RectF(0,0,bmpSrcWidth,bmpSrcHeight), p);//绘制蒙版bitmap(PS:圆角的半径的单位是像素)
break;
default:
break;
}
p.setXfermode(xfermode);
canvas.drawBitmap(bitmapSrc, null, new RectF(0,0,bmpSrcWidth, bmpSrcHeight), p);//绘制原图并相交得出最终圆角图片
p.setXfermode(null);
return bmpDst;
}
@Override
protected void onDraw(Canvas canvas) {
if(getDrawable() instanceof BitmapDrawable && getDrawable()!=null){
mBmpDrawable = (BitmapDrawable) getDrawable();
if(!mBmpDrawable.getBitmap().equals(mShapeBmp)){
mWidth = getWidth();
mHeight = getHeight();
mShapeBmp = getShapeBitmap(mWidth,mHeight, mBmpDrawable.getBitmap(), mShape);
BitmapDrawable drawable = new BitmapDrawable(mShapeBmp);
setImageDrawable(drawable);
return;
}
}
super.onDraw(canvas);
}
}</span>
实现原理:
1.绘制与原图同样大小的几何蒙板
2.在该蒙板上绘制原图,并利用paint对象的PorterDuffXfermode图层交互特性,仅绘制出原图与蒙版相交的区域
相比继承View的方法,这种方法比较大的好处在于,当我们需要静态设置图片的时候,只需要在XML布局文件中相应的src属性上添加,而免去了要在java代码中手动设置的麻烦,减少了代码的耦合度,同时还能享受原来ImageView的各种特性。