本文參考自大神張鴻洋的文章,同時修復了一個我自己發現的問題(因爲不知道算不算Bug):當顯示圓形圖片時,若在xml中設置的圖片的寬高屬性爲wrap_content,而所用的圖片寬高比不爲1。當width>height時,圖片只顯示從左邊開始長度爲height值的部分;當height>width時,圖片只顯示從上邊開始高度爲width值的部分;所以我做了一點處理,使其達到類似於 android:scaleType=”center”的功能。
圖片顯示效果
demo地址爲:Android自定義圓形圓角圖片示例
這裏只貼自定義View代碼如下:
package com.android.circularpicture;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.ImageView;
/**
* 自定義圓形ImageView
* 作者: k.k on 2017/8/29.
* 郵箱:[email protected]
*/
public class CircularImageView extends ImageView {
//圖片類型(circle/round)
private int type;
private Bitmap mImg;//圖片
private int mWidth;//控件的寬度
private int mHeight;//控件的高度
private int mRadius;//圓角的半徑
private Bitmap circleView;//加載的圓形View
private Bitmap roundView;//加載的圓角View
/*這個構造方法必須要*/
public CircularImageView(Context context) {
this(context, null);
}
/*這個構造方法必須要*/
public CircularImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/*真正的執行在這裏*/
public CircularImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray typedarray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircularImageView, defStyle, 0);
int counts = typedarray.getIndexCount();
for (int i = 0; i < counts; i++) {
int attr = typedarray.getIndex(i);
switch (attr) {
case R.styleable.CircularImageView_src:
mImg = BitmapFactory.decodeResource(getResources(), typedarray.getResourceId(attr, 0));
break;
case R.styleable.CircularImageView_borderRadius:
int defValue = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 10f, getResources().getDisplayMetrics()
);//applyDimension()是一個將各種單位的值轉換爲像素的方法,第一個參數是指第二個參數值得單位,並將該單位的值轉換爲px,在這裏TypedValue.COMPLEX_UNIT_DIP表示dp,即10f表示10dp
mRadius = typedarray.getDimensionPixelSize(attr, defValue);
break;
case R.styleable.CircularImageView_type:
type = typedarray.getInt(attr, 0);
break;
}
}
typedarray.recycle();//記得回收掉
}
//重寫onMeasure方法,計算控件的寬度和高度
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/*設置寬度*/
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
//類型:match parent(繼承父佈局的值),accurate(精確的值)
if (specMode == MeasureSpec.EXACTLY) {
mWidth = specSize;//此時,控件的寬度即爲specSize
} else {
//由控件自身決定寬度
int demandWidthImg = getPaddingLeft() + getPaddingRight() + mImg.getWidth();
//類型:wrap_parent
if (specMode == MeasureSpec.AT_MOST) {
mWidth = Math.min(demandWidthImg, specSize);//返回較小的那個數值
}
//這種情況很少使用到
else {
mWidth = specSize;
}
}
/*設置高度*/
specMode = MeasureSpec.getMode(heightMeasureSpec);
specSize = MeasureSpec.getSize(heightMeasureSpec);
if (specMode == MeasureSpec.EXACTLY) {
mHeight = specSize;
} else {
int demandHeightImg = getPaddingTop() + getPaddingBottom() + mImg.getHeight();
if (specMode == MeasureSpec.AT_MOST) {
mHeight = Math.min(demandHeightImg, specSize);
} else {
mHeight = specSize;
}
}
setMeasuredDimension(mWidth, mHeight);//設置View寬高的值(實際叫測量值)
}
/*重寫onDraw方法,繪製對應的圖片*/
@Override
protected void onDraw(Canvas canvas) {
float horizontalOffset = mWidth > mHeight ? (mWidth - mHeight) / 2 : (mHeight - mWidth) / 2;//需要調整的偏移量
switch (type) {
//繪製圓形圖片
case Constant.CIRCLE_TYPE:
int diameter = Math.min(mWidth, mHeight);//因爲是繪製圓形圖片,如果mWidth,mHeight不一致,取小的值作爲圓圖的直徑
mImg = Bitmap.createScaledBitmap(mImg, diameter, diameter, false);//以w,h爲目標對bitmap進行縮放,filter表示是否要對位圖進行過濾(濾波)處理這個方法的效果就類似於ImageView.ScaleType.FIT_XY
if (mWidth > mHeight) {
canvas.drawBitmap(createCircleView(mImg, diameter), horizontalOffset, 0, null);
} else {
canvas.drawBitmap(createCircleView(mImg, diameter), 0, horizontalOffset, null);
}
circleView = null;
break;
//繪製帶圓角的圖片
case Constant.ROUND_TYPE:
canvas.drawBitmap(createRoundView(mImg), 0, 0, null);
roundView = null;
break;
}
}
/*繪製圓形View*/
private Bitmap createCircleView(Bitmap bitmap, int diameter) {
Paint paint = new Paint();
paint.setAntiAlias(true);//設置抗鋸齒方法
circleView = Bitmap.createBitmap(diameter, diameter, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(circleView);//產生一個同樣大小的畫布
canvas.drawCircle(diameter / 2, diameter / 2, diameter / 2, paint);//繪製圓形
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, 0, 0, paint);
return circleView;
}
/*繪製圓角View*/
private Bitmap createRoundView(Bitmap bitmap) {
final Paint paint = new Paint();
paint.setAntiAlias(true);
roundView = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(roundView);
RectF rect = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
canvas.drawRoundRect(rect, mRadius, mRadius, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, 0, 0, paint);
return roundView;
}
}