這才是真正的萬能圓角ImageView
本文已授權我的公眾號:我就是馬雲飛 獨家釋出
不知道有沒有人記得我去年寫過一個圓角的imageview。不知道的可以先去看看:萬能圓角imagview,本文是基於上一篇的內容進行新增以及修改的。不然直接看這篇可能會有點懵。
前言
我為什麼要二次封裝?最近公司有個需求是這樣的。
同事說,不知道怎麼搞,於是乎,我把之前寫的imageview給過去了。他來了句,你這圓角和fitxy同時設定會有問題啊,我反手就是一個大嘴巴子。我的程式碼怎麼會有問題。於是,拿來一瞧,的確有點問題。So,我決定對這個imageview進行二次的封裝。(當然了,這個問題的最後處理是後臺直接給一個圓角的imageview)。
如何實現
細想一下,上文我們是怎麼做的,我們是把繪製的區域,從(0,0)移動到我們想要的地方,說個粗暴點的話,我們強制的把這個imagview的scaletype的屬性設定了centercrop。那麼這次我們就要將它的scaletype設定成可調的屬性。
實現邏輯
我前面也說過了,上次我們是根據imageview的原始碼來修改他的編輯區域的,這次,我們照常開啟原始碼,找到園中對scaletype的處理邏輯,程式碼如下:
private void configureBounds() {
if (mDrawable == null || !mHaveFrame) {
return ;
}
final int dwidth = mDrawableWidth;
final int dheight = mDrawableHeight;
final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
final int vheight = getHeight() - mPaddingTop - mPaddingBottom;
final boolean fits = (dwidth < 0 || vwidth == dwidth)
&& (dheight < 0 || vheight == dheight);
if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
/* If the drawable has no intrinsic size, or we're told to
scaletofit, then we just fill our entire view.
*/
mDrawable.setBounds(0, 0, vwidth, vheight);
mDrawMatrix = null;
} else {
// We need to do the scaling ourself, so have the drawable
// use its native size.
mDrawable.setBounds(0, 0, dwidth, dheight);
if (ScaleType.MATRIX == mScaleType) {
// Use the specified matrix as-is.
if (mMatrix.isIdentity()) {
mDrawMatrix = null;
} else {
mDrawMatrix = mMatrix;
}
} else if (fits) {
// The bitmap fits exactly, no transform needed.
mDrawMatrix = null;
} else if (ScaleType.CENTER == mScaleType) {
// Center bitmap in view, no scaling.
mDrawMatrix = mMatrix;
mDrawMatrix.setTranslate(Math.round((vwidth - dwidth) * 0.5f),
Math.round((vheight - dheight) * 0.5f));
} else if (ScaleType.CENTER_CROP == mScaleType) {
mDrawMatrix = mMatrix;
float scale;
float dx = 0, dy = 0;
if (dwidth * vheight > vwidth * dheight) {
scale = (float) vheight / (float) dheight;
dx = (vwidth - dwidth * scale) * 0.5f;
} else {
scale = (float) vwidth / (float) dwidth;
dy = (vheight - dheight * scale) * 0.5f;
}
mDrawMatrix.setScale(scale, scale);
mDrawMatrix.postTranslate(Math.round(dx), Math.round(dy));
} else if (ScaleType.CENTER_INSIDE == mScaleType) {
mDrawMatrix = mMatrix;
float scale;
float dx;
float dy;
if (dwidth <= vwidth && dheight <= vheight) {
scale = 1.0f;
} else {
scale = Math.min((float) vwidth / (float) dwidth,
(float) vheight / (float) dheight);
}
dx = Math.round((vwidth - dwidth * scale) * 0.5f);
dy = Math.round((vheight - dheight * scale) * 0.5f);
mDrawMatrix.setScale(scale, scale);
mDrawMatrix.postTranslate(dx, dy);
} else {
// Generate the required transform.
mTempSrc.set(0, 0, dwidth, dheight);
mTempDst.set(0, 0, vwidth, vheight);
mDrawMatrix = mMatrix;
mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
}
}
}
我們找到上次對其實位置修改的地方。跟著原始碼一樣改成一樣。改完之後程式碼如下:
if (drawablewidth <= 0 || drawableheight <= 0) {
drawable.setBounds(0, 0, viewwidth, viewheight);
matrix = null;
} else {
drawable.setBounds(0, 0, drawablewidth, drawableheight);
if (ScaleType.MATRIX == getScaleType()) {
if (matrix.isIdentity()) {
matrix = null;
}
} else if (fits) {
matrix = null;
} else if (ScaleType.CENTER == getScaleType()) {
matrix.setTranslate(Math.round((viewwidth - drawablewidth) * 0.5f),
Math.round((viewheight - drawableheight) * 0.5f));
} else if (ScaleType.CENTER_CROP == getScaleType()) {
if (drawablewidth * viewheight > viewwidth * drawableheight) {
dx = (viewwidth - drawablewidth * scale) * 0.5f;
} else {
dy = (viewheight - drawableheight * scale) * 0.5f;
}
matrix.setScale(scale, scale);
matrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
} else if (ScaleType.CENTER_INSIDE == getScaleType()) {
if (drawablewidth <= viewwidth && drawableheight <= viewheight) {
scale = 1.0f;
} else {
scale = Math.min((float) viewwidth / (float) drawablewidth,
(float) viewheight / (float) drawableheight);
}
dx = Math.round((viewwidth - drawablewidth * scale) * 0.5f);
dy = Math.round((viewheight - drawableheight * scale) * 0.5f);
matrix.setScale(scale, scale);
matrix.postTranslate(dx, dy);
} else {
if (drawablewidth * viewheight > viewwidth * drawableheight) {
dx = (viewwidth - drawablewidth * scale) * 0.5f;
} else {
dy = (viewheight - drawableheight * scale) * 0.5f;
}
matrix.setScale(scale, scale);
matrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
}
}
if (ScaleType.FIT_XY == getScaleType() && matrix != null) {
scale1 = viewwidth * 1.0f / drawablewidth;
scale2 = viewheight * 1.0f / drawableheight;
matrix.setScale(scale1, scale2);
}
bitmapShader.setLocalMatrix(matrix);
paint.setShader(bitmapShader);
我們可以發現其實和原始碼對比下來,改動還是有的。為什麼呢?我們仔細看下這段程式碼:
if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
/* If the drawable has no intrinsic size, or we're told to
scaletofit, then we just fill our entire view.
*/
mDrawable.setBounds(0, 0, vwidth, vheight);
mDrawMatrix = null;
}
原始碼裡面只對drawable進行了處理。但我們可以發現後面的判斷每次都會呼叫matrix.setScale這個方法。但在當scaletype為fitxy時,沒有進行處理。我們也知道,我們看的是原始碼,他肯定偷偷的在某個地方進行了處理。那麼我們要處理怎麼辦呢?仔細看程式碼:
if (ScaleType.FIT_XY == getScaleType() && matrix != null) {
scale1 = viewwidth * 1.0f / drawablewidth;
scale2 = viewheight * 1.0f / drawableheight;
matrix.setScale(scale1, scale2);
}
fitxy我們都知道是充滿布局,然後在細看這塊程式碼,你是不是懂了呢?佈局的寬高除以圖片的寬高。然後設定它的比例。
加上邊框
為了更好的封裝,我選擇加上邊框和邊框顏色的自定義屬性。那麼接下來就是直接上程式碼了。
我們需要再定義一個畫筆:
boder_paint.setAntiAlias(true);
boder_paint.setStyle(Paint.Style.STROKE);
boder_paint.setColor(border_color);
boder_paint.setStrokeWidth(border_width);
接下來我們就是直接畫上去了。當然了,我們這邊預設是不設定,也就是borderwidth為0,所以我們要加一個判斷:
if (type == TYPE_ROUND) {
canvas.drawRoundRect(rectF, borderRadius, borderRadius,paint);
if (border_width > 0) {
canvas.drawRoundRect(rectF, borderRadius, borderRadius, boder_paint);
}
} else if (type == TYPE_CIRCLE) {
canvas.drawCircle(radius, radius, radius, paint);
if (border_width > 0) {
canvas.drawCircle(radius, radius, radius, boder_paint);
}
} else {
getDrawable().draw(canvas);
}
我們一編譯,一執行,效果炸了。你問我為什麼?我們先來看個效果在說把。
我們發現我們修改的fitxy屬性已經生效了。但是,為什麼加了邊框是這樣呢?
仔細想想。我們畫圓角和圓的時候是不是忘記去掉了邊框的寬度呢?那麼我們既然找到了原因就可以找到解決方法了。那我們就直接去掉邊框的高度,注意!!圓角和圓的都要處理。
canvas.drawCircle(radius, radius, radius, paint);
if (border_width > 0) {
canvas.drawCircle(radius, radius, radius - border_width / 2, boder_paint);
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (type == TYPE_ROUND) {
rectF = new RectF(border_width/2,border_width/2, getWidth()- border_width / 2, getHeight()- border_width / 2);
}
}
我們再來看下效果:
搞定。完美~
使用
在gradle加上如下程式碼:
compile 'com.angel:SWImageView:1.0.0'
關於自定義屬性:
<declare-styleable name="SWImageView">
<attr name="borderRadius" format="dimension"/>
<attr name="type">
<enum name="normal" value="-1"/>
<enum name="circle" value="0"/>
<enum name="round" value="1"/>
</attr>
<attr name="borderWidth" format="dimension" />
<attr name="borderColor" format="integer" />
</declare-styleable>