項目裏需要用到圓形ImageView,網上查了資料後發現用自定義控件是最完美的解決方案,不需要額外複製剪切什麼的,這個輪子已經造的很好了,讓我們來好好體會並領略一下。文章主要內容轉載自啊左不是蝸牛
感謝蝸牛同學的精彩註釋,在此處另外附上GitHub上大神的超完整版的各種圓形ImageView的地址,感興趣的同學可以去看看。
package com.baby.tools;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Bitmap.Config;
import android.graphics.PorterDuff.Mode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.util.AttributeSet;
import android.widget.ImageView;
import com.example.babybaycar.R;
public class RoundImage extends ImageView {
//(1)聲明應用環境
private Context mContext;
//(1)聲明圓環厚度
private int mBorderThickness = 0;
//(1)聲明內外邊框顏色
private int mBorderInsideColor = 0;
private int mBorderOutsideColor = 0;
//(1)聲明並初始化圓形的默認長寬
private int mBorderWidth = 0;
private int mBorderHeight = 0;
//(1)聲明默認顏色值是黑色
private int defaultColor = 0xFFFFFFFF;
public RoundImage(Context context) {
super(context);
mContext = context;
}
public RoundImage(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
setRoundAttribute(attrs);
}
public RoundImage(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
//(3)在構造方法裏,設置圓形的默認屬性
setRoundAttribute(attrs);
}
/*
* (2)設置這個圓環的厚度,以及內邊框和外邊框的顏色;
* 由於android 沒有這個圓環,當然我們得自定義它的屬性
* 所以在res/values/attrs.xml 文件裏自定義邊框厚度和
* 內外邊框的顏色,然後在下面這個函數裏去設定具體的值
*/
private void setRoundAttribute(AttributeSet attrs){
/*
* TypedArray 顧名思義,被定義了類型的數組,因此它就是
* 一個用來裝載自定義的屬性值的容器,被 obtainStyledAttributes(AttributeSet, int[], int, int)
* obtainAttributes(AttributeSet, int[]).所調用;google
* 說,使用結束後記得調用recycle()來釋放它
*/
TypedArray myAttrs = mContext.obtainStyledAttributes(attrs,
R.styleable.roundedimageview);
mBorderInsideColor = myAttrs.getColor(
R.styleable.roundedimageview_border_inside_color,defaultColor);
mBorderOutsideColor = myAttrs.getColor(
R.styleable.roundedimageview_border_outside_color, defaultColor);
mBorderThickness = myAttrs.getDimensionPixelSize(
R.styleable.roundedimageview_border_thickness,0);
}
/*
* (4)繪製圓形
* 由於Google在ImageView中已經有了onDraw方法是專門用來繪製圖形的,
* 所以這裏我們不必自己新建方法,而直接重寫父類ImageView的onDraw
*/
@Override
protected void onDraw(Canvas canvas) {
/*
* 聲明一個可畫的對象,並從ImageView對象中
* 獲取一個具體的對象(沒有即爲null),即實例化
*/
Drawable drawable = getDrawable();
//如果我們要畫的圖片爲空,那麼就沒有繼續畫圓環的必要了,返回
if (drawable == null) {
return;
}
//如果我們要畫的圖片的寬度或高度不符合要求,也沒有畫的必要
if (getWidth() == 0 || getHeight()==0) {
return;
}
/*
* 同時應該注意到9Patch的圖片也是有邊框的,那麼我們要加邊框的
* 圖片的應用類是繼承了 NinePathcDrawable 的話,那麼人家本身
* 有邊框,我們也就沒有加圓形邊框的必要了,這個是個人理解;
* 不曉得對否
*/
if(drawable.getClass() == NinePatchDrawable.class){
return;
}
/*
* 參考Google 官方文檔,Android 畫一個view 要經歷兩個過程
* 第一個是計算measure ,即計算這個view 所需要多大的尺寸空間
* 第二個是佈局layout ,即要根據開發者的要求來放在哪個位置
* 所以,這裏我們要先測量
*/
this.measure(0, 0);
//獲得該圖片,轉換成Bitmap 格式,好處理
Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();
//要知道圖片大小,才能計算出接下來所畫圓形的半徑
if (mBorderWidth == 0) {
mBorderWidth = getWidth();
}
if (mBorderHeight == 0) {
mBorderHeight = getHeight();
}
//半徑
int radius = 0;
/*
* 這裏要根據我們在layout中是否設置了內外邊框顏色的需求而動手畫圓
* 三種情況:A:border_inside_color 和 border_outside_color 什麼都不設
* B:設置了外邊框
* C:設置了內外邊框,這時候就意味着要畫個圓環了
*/
//取寬度與高度中最大的值爲參考,注意畫外圓時加上圓環厚度
if (mBorderInsideColor != defaultColor
&& mBorderOutsideColor != defaultColor) {
radius = (mBorderWidth < mBorderHeight ? mBorderWidth
: mBorderHeight) / 2 - 2 * mBorderThickness;
// 畫內圓
drawCircleBorder(canvas, radius + mBorderThickness / 2,
mBorderInsideColor);
// 畫外圓
drawCircleBorder(canvas, radius + mBorderThickness
+ mBorderThickness / 2, mBorderOutsideColor);
} else if (mBorderInsideColor != defaultColor
&& mBorderOutsideColor == defaultColor) {// 定義畫一個邊框
radius = (mBorderWidth < mBorderHeight ? mBorderWidth
: mBorderHeight) / 2 - mBorderThickness;
drawCircleBorder(canvas, radius + mBorderThickness / 2,
mBorderInsideColor);
} else if (mBorderInsideColor == defaultColor
&& mBorderOutsideColor != defaultColor) {// 定義畫一個邊框
radius = (mBorderWidth < mBorderHeight ? mBorderWidth
: mBorderHeight) / 2 - mBorderThickness;
drawCircleBorder(canvas, radius + mBorderThickness / 2,
mBorderOutsideColor);
} else {// 沒有邊框
radius = (mBorderWidth < mBorderHeight ? mBorderWidth
: mBorderHeight) / 2;
}
//畫圓形圖像
Bitmap roundBitmap = getCroppedRoundBitmap(bitmap, radius);
canvas.drawBitmap(roundBitmap, mBorderWidth/2 - radius,
mBorderHeight/2 - radius, null);
}
/**
* 根據顏色和半徑畫圓邊框,中間是空的
*/
private void drawCircleBorder(Canvas canvas,int radius,int color) {
//新建畫筆對象
Paint paint = new Paint();
/*
* 先來了解下畫筆的屬性方法
* setAntiAlias: 設置畫筆的鋸齒效果,true則去掉鋸齒;
* setColor: 設置畫筆顏色;
* setARGB: 設置畫筆的a,r,p,g值。
* setAlpha: 設置Alpha值,即透明度
* setTextSize: 設置字體尺寸
* setStyle: 設置畫筆風格,空心或者實心;
* setStrokeWidth: 設置空心的邊框寬度。
* setFilterBitmap 如設爲true,則圖像在動畫進行中會濾掉對Bitmap圖像的優化操作,加快顯示
* setDither 設定是否使用圖像抖動處理,會使繪製出來的圖片顏色更加平滑和飽滿,圖像更加清晰
*/
//這裏我們不要鋸齒(難看),處理抖動,加快顯示,空心,再設置顏色和邊框寬度
paint.setAntiAlias(true);
paint.setColor(color);
paint.setDither(true);
paint.setFilterBitmap(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(mBorderThickness);
canvas.drawCircle(mBorderWidth/2, mBorderHeight/2, radius, paint);
}
/**
* 獲取裁剪後的圓形圖片
* 1、得到位於中間位置的正方形圖片
* 2、在正方形圖片的基礎上再根據半徑進行裁剪,使用了Bitmap.createScaledBitmap(squareBitmap, diameter,
diameter, true);
* @param radius
* 半徑
*/
public Bitmap getCroppedRoundBitmap(Bitmap bmp, int radius) {
Bitmap scaledSrcBmp;
int diameter = radius * 2;
// 爲了防止寬高不相等,造成圓形圖片變形,因此截取長方形中處於中間位置最大的正方形圖片
int bmpWidth = bmp.getWidth();
int bmpHeight = bmp.getHeight();
int squareWidth = 0, squareHeight = 0;
int x = 0, y = 0;
Bitmap squareBitmap;
if (bmpHeight > bmpWidth) {// 高大於寬
squareWidth = squareHeight = bmpWidth;
x = 0;
y = (bmpHeight - bmpWidth) / 2;
// 截取正方形圖片
squareBitmap = Bitmap.createBitmap(bmp, x, y, squareWidth,
squareHeight);
} else if (bmpHeight < bmpWidth) {// 寬大於高
squareWidth = squareHeight = bmpHeight;
x = (bmpWidth - bmpHeight) / 2;
y = 0;
squareBitmap = Bitmap.createBitmap(bmp, x, y, squareWidth,
squareHeight);
} else {
squareBitmap = bmp;
}
if (squareBitmap.getWidth() != diameter
|| squareBitmap.getHeight() != diameter) {
scaledSrcBmp = Bitmap.createScaledBitmap(squareBitmap, diameter,
diameter, true);
} else {
scaledSrcBmp = squareBitmap;
}
Bitmap output = Bitmap.createBitmap(scaledSrcBmp.getWidth(),
scaledSrcBmp.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
Paint paint = new Paint();
Rect rect = new Rect(0, 0, scaledSrcBmp.getWidth(),
scaledSrcBmp.getHeight());
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawCircle(scaledSrcBmp.getWidth() / 2,
scaledSrcBmp.getHeight() / 2, scaledSrcBmp.getWidth() / 2,
paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(scaledSrcBmp, rect, rect, paint);
// bitmap回收(recycle導致在佈局文件XML看不到效果)
// bmp.recycle();
// squareBitmap.recycle();
// scaledSrcBmp.recycle();
bmp = null;
squareBitmap = null;
scaledSrcBmp = null;
return output;
}
}