自定義View(1)--圓形圖片、圓角圖片的實現
阿新 • • 發佈:2019-02-08
之前說過會將專案中運用的東西抽離出來做一個總結,今天我主要想總結一下圓角和圓形頭像問題。由於我們的應用涉及到很多使用者頭像,如果所有的影象都是方方正正的話,那顯得不是很美觀,所以設計溼強行要我將頭像圓角化處理。好吧,so easy。專案截圖我就不想貼了,還是一貫貼demo截圖吧,如下:
看著效果還行啊,下面我們就講講怎麼去實現它。
一、自定義ImageView顯示
1.自定義View屬性
自定義view,經常會涉及到view的屬性問題,很簡單,按以下操作你就能輕鬆實現自定義屬性了。首先你得在資原始檔裡的values目錄下,新建一個檔案attrs,宣告你想自定義的屬性。
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="customParams"> <attr name="isCircle" format="boolean" /> <attr name="isRoundCorner" format="boolean" /> <attr name="corner_radius" format="dimension" /> </declare-styleable> </resources>
至於declare-styleable的format,需要了解的同學,可以百度一下,很多文章有介紹哦(點這裡檢視)。其次,你需要在建構函式去關聯這些屬性,才能在畫圖過程中使用到他們。
這裡有人會TypedArray為啥要recycle呢,說實話我也納悶了,特地去查了一下,官方的解釋是:回收TypedArray,以便後面重用。在呼叫這個函式後,你就不能再使用這個TypedArray。在TypedArray後呼叫recycle主要是為了快取。當recycle被呼叫後,這就說明這個物件從現在可以被重用了。TypedArray 內部持有部分陣列,它們快取在Resources類中的靜態欄位中,這樣就不用每次使用前都需要分配記憶體。你可以看看TypedArray.recycle()中的程式碼:public CustomImageView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } private void init(Context context, AttributeSet attrs) { if (attrs != null) { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.customParams); isCircle = a.getBoolean(R.styleable.customParams_isCircle, false); isRoundCorner = a.getBoolean(R.styleable.customParams_isRoundCorner, false); corner_radius = a.getDimension(R.styleable.customParams_corner_radius, 0); // 回收TypedArray,以便後面重用 a.recycle(); } }
想了解更多的話,可以點選這裡去看看哦/** * Give back a previously retrieved StyledAttributes, for later re-use. */ public void recycle() { synchronized (mResources.mTmpValue) { TypedArray cached = mResources.mCachedStyledAttributes; if (cached == null || cached.mData.length < mData.length) { mXml = null; mResources.mCachedStyledAttributes = this; } } }
2.圖片繪畫過程onDraw
這是本篇文章的重點,自定義View你必須去重寫onDraw(Canvas canvas),在其中去實現你所需要的邏輯,程式碼如下:
@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
if (getWidth() == 0 || getHeight() == 0) {
return;
}
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
if (defaultWidth == 0) {
defaultWidth = getWidth();
}
if (defaultHeight == 0) {
defaultHeight = getHeight();
}
if (isCircle || isRoundCorner) {// 圓形或圓角
int radius = 0;
radius = (defaultWidth < defaultHeight ? defaultWidth : defaultHeight) / 2;
Bitmap roundBitmap = getCroppedRoundBitmap(bitmap, radius);
canvas.drawBitmap(roundBitmap, defaultWidth / 2 - radius, defaultHeight / 2 - radius, null);
} else {
super.onDraw(canvas);
}
}
程式碼比較簡單,我就不一一去介紹了,當佈局檔案該view被宣告成了圓角或圓形view時,則重新畫圖。這裡有個最關鍵的方法getCroppedRoundBitmap,需要註釋下。
/**
* 獲取裁剪後的圓形圖片
*
* @param bmp
* 原圖
* @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);
if (isRoundCorner) {
// 畫圓角
RectF rectF = new RectF(rect);
float r = corner_radius;
if (corner_radius == 0)
r = radius / 4;// 設定圓角預設值
canvas.drawRoundRect(rectF, r, r, paint);
} else {
// 畫圓形圖片
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);
bmp = null;
squareBitmap = null;
scaledSrcBmp = null;
return output;
}
仔細讀下來,發現其實程式碼不是很難。主要是運用到了Xfermode的疊加顯示效果,先將獲得的方形圖片在畫布上顯示,在畫布上繼續畫上一個圓形或者圓角的圖片,new PorterDuffXfermode(Mode.SRC_IN)取兩層繪製交集,顯示上層。需要了解更多有關Xfermode的可以點選這裡去學學哦。
ok,自定義view顯示圓形圖片,到這裡就結束了。有問題的提出來,大家一起學習哈!
二、自定義Drawable來顯示圓形、圓角圖片
顯示圓形圖片還有其他方法,在此順便學習了別人的部落格並介紹下如何使用自定義Drawable。
相對於自定義view,使用Drawable 又有哪些優點呢?
1.自定義Drawable,相比View來說,Drawable屬於輕量級的、使用也很簡單
2.自定義drawableU效能更加
那麼怎麼用呢,不說了直接上程式碼
public class CustomDrawable extends Drawable {
private Paint mPaint;
private int mWidth;
private Bitmap mBitmap;
private RectF rectF;
private int mType = 0;// 0表示正常,1表示圓形,其他表示圓角
private float mRadius = 50;
public CustomDrawable(Bitmap bitmap, int type) {
mBitmap = bitmap;
BitmapShader bitmapShader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setShader(bitmapShader);
mWidth = Math.min(mBitmap.getWidth(), mBitmap.getHeight());
mType = type;
}
public CustomDrawable setRadius(float radius) {
this.mRadius = radius;
return this;
}
@Override
public void setBounds(int left, int top, int right, int bottom) {
super.setBounds(left, top, right, bottom);
rectF = new RectF(left, top, right, bottom);
}
@Override
public void draw(Canvas canvas) {
if (mType == 1)
canvas.drawCircle(mWidth / 2, mWidth / 2, mWidth / 2, mPaint);
else {
canvas.drawRoundRect(rectF, mRadius, mRadius, mPaint);
}
}
@Override
public int getIntrinsicWidth() {
return mWidth;
}
@Override
public int getIntrinsicHeight() {
return mWidth;
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
讀下來發現程式碼不難哈,核心的思想和前面提到的自定View差不多,在此就不累贅了。直接看下如何使用它吧。
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView iv_circle = (ImageView) findViewById(R.id.iv_circle);
ImageView iv_roundcorner = (ImageView) findViewById(R.id.iv_roundcorner);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.girl1);
iv_circle.setImageDrawable(new CustomDrawable(bitmap, 1));
iv_roundcorner.setImageDrawable(new CustomDrawable(bitmap, 2).setRadius(dip2px(50)));
}
/**
* Dip轉Px
*
* @param context
* @param dipValue
* @return
*/
public int dip2px(float dipValue) {
try {
final float scale = getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
} catch (Exception ex) {
ex.printStackTrace();
return 0;
}
}
使用是不是很簡單哈,哈哈,相信大家也都學會了。由於篇幅關係,佈局檔案就不貼了。
舒口氣,自定義圓形、圓角圖片就講到這裡了。有問題的可以提出來,大家一起探討哦。我也是一隻邊學邊記錄的菜鳥,歡迎騷擾。
最後感謝一下鴻洋的部落格,他寫的更好,大夥可以去學學!