自定義view實現圓形頭像功能
阿新 • • 發佈:2018-12-20
個人中心是每一個app都有的模組,在個人中心中,我們常常可以看到一個圓形有邊框(無邊框)的頭像。作為一個常見的功能,今天我們把它實現一下。
1. 需求分析
這個主要分為兩部分:一個頭像邊框,一個是圓形頭像顯示,我們主要是考慮採用自定義View來實現這個需求。
2. 實現過程
廢話不多說,看一下程式碼。
2.1 新建一個View類
我們新建一個類,命名為RoundImageView,然後讓這個類繼承ImageView / AppCompatImageView(5.0以後官方推薦AppCompatImageView)
public class RoundImageView extends android.support.v7.widget.AppCompatImageView
接著,重寫View方法,程式碼如下:
public RoundImageView(Context context) { super(context); } public RoundImageView(Context context, AttributeSet attrs) { super(context, attrs); } public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }
2.2 View流程
- 首先通過setImageXxx()方法設定圖片Bitmap;
- 進入建構函式RoundImageView()獲取自定義引數,以及呼叫setCustomAttributes()函式;
- 進入setCustomAttributes()函式(非常關鍵)進行圖片畫筆邊界畫筆(Paint)一些重繪引數初始化:構建渲染器BitmapShader用Bitmap來填充繪製區域,設定樣式和內外圓半徑計算等,以及呼叫updateShaderMatrix()函式和 invalidate()函式;
- 觸發ondraw()函式完成最終的繪製。使用配置好的Paint先畫出繪製內圓形來以後再畫邊界圓形。
- 獲取剪裁後的圖片(剪裁後圖片為圓形)
- 邊緣畫圓,實現邊框有無需求
上面流程中,除了第3、5、6步,其餘幾步都是自定義View的標準流程,這裡不詳細說了,請自行百度自定義View。
/**
* Created by hangli on 2018/11/8.
* 自定義View頭像框
* 圓形ImageView,可設定最多兩個寬度不同且顏色不同的圓形邊框。
* 設定顏色在xml佈局檔案中由自定義屬性配置引數指定
*/
public class RoundImageView extends android.support.v7.widget.AppCompatImageView {
private int mBorderThickness = 0;
private Context mContext;
private int defaultColor = 0xFFFFFFFF;
// 如果只有其中一個有值,則只畫一個圓形邊框
private int mBorderOutsideColor = 0;
private int mBorderInsideColor = 0;
// 控制元件預設長、寬
private int defaultWidth = 0;
private int defaultHeight = 0;
public RoundImageView(Context context) {
super(context);
}
public RoundImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void setCustomAttributes(AttributeSet attrs) {
TypedArray a = mContext.obtainStyledAttributes(attrs,R.styleable.roundedimageview);
mBorderThickness = a.getDimensionPixelSize(R.styleable.roundedimageview_border_thickness, 0);
mBorderOutsideColor = a.getColor(R.styleable.roundedimageview_border_outside_color,defaultColor);
mBorderInsideColor = a.getColor(R.styleable.roundedimageview_border_inside_color, defaultColor);
}
@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable() ;
if (drawable == null) {
return;
}
if (getWidth() == 0 || getHeight() == 0) {
return;
}
this.measure(0, 0);
if (drawable.getClass() == NinePatchDrawable.class)
return;
Bitmap b = ((BitmapDrawable) drawable).getBitmap();
Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);
if (defaultWidth == 0) {
defaultWidth = getWidth();
}
if (defaultHeight == 0) {
defaultHeight = getHeight();
}
int radius = 0;
if (mBorderInsideColor != defaultColor && mBorderOutsideColor != defaultColor) {// 定義畫兩個邊框,分別為外圓邊框和內圓邊框
radius = (defaultWidth < defaultHeight ? defaultWidth : defaultHeight) / 2 - 2 * mBorderThickness;
// 畫內圓
drawCircleBorder(canvas, radius + mBorderThickness / 2,mBorderInsideColor);
// 畫外圓
drawCircleBorder(canvas, radius + mBorderThickness + mBorderThickness / 2, mBorderOutsideColor);
} else if (mBorderInsideColor != defaultColor && mBorderOutsideColor == defaultColor) {// 定義畫一個邊框
radius = (defaultWidth < defaultHeight ? defaultWidth : defaultHeight) / 2 - mBorderThickness;
drawCircleBorder(canvas, radius + mBorderThickness / 2, mBorderInsideColor);
} else if (mBorderInsideColor == defaultColor && mBorderOutsideColor != defaultColor) {// 定義畫一個邊框
radius = (defaultWidth < defaultHeight ? defaultWidth : defaultHeight) / 2 - mBorderThickness;
drawCircleBorder(canvas, radius + mBorderThickness / 2, mBorderOutsideColor);
} else {// 沒有邊框
radius = (defaultWidth < defaultHeight ? defaultWidth : defaultHeight) / 2;
}
Bitmap roundBitmap = getCroppedRoundBitmap(bitmap, radius);
canvas.drawBitmap(roundBitmap, defaultWidth / 2 - radius, defaultHeight / 2 - radius, null);
}
/**
* 獲取裁剪後的圓形圖片
*/
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(),
Bitmap.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(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(scaledSrcBmp, rect, rect, paint);
bmp = null;
squareBitmap = null;
scaledSrcBmp = null;
return output;
}
/**
* 邊緣畫圓
*/
private void drawCircleBorder(Canvas canvas, int radius, int color) {
Paint paint = new Paint();
/* 去鋸齒 */
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
paint.setColor(color);
/* 設定paint的 style 為STROKE:空心 */
paint.setStyle(Paint.Style.STROKE);
/* 設定paint的外框寬度 */
paint.setStrokeWidth(mBorderThickness);
canvas.drawCircle(defaultWidth / 2, defaultHeight / 2, radius, paint);
}
}
2.3 定義配置檔案
在values目錄下,新建一個attr.xml的檔案用於配置。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="roundedimageview">
<attr name="border_thickness" format="dimension" />
<attr name="border_inside_color" format="color" />
<attr name="border_outside_color" format="color"></attr>
</declare-styleable>
</resources>
2.4 在佈局檔案中使用控制元件
<com.example.hanglidemo.RoundImageView
android:id="@+id/headCv"
android:layout_width="65dp"
android:layout_height="65dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="40dp"
imagecontrol:border_inside_color="#ffffff"
imagecontrol:border_outside_color="#ffffff"
imagecontrol:border_thickness="1dp"
android:src="@mipmap/mine_default_teater_logo"/>