Android ImageLoader 顯示圓角圖片,可指定圖片某幾個角為圓角
原文地址:http://blog.csdn.net/urmytch/article/details/52231419
Android中實現圓角圖片的方式有很多種:
- 一、shape
- 二、.9圖
- 三、XferMode
- 四、BitmapShader
- 五、ClipPath
其中一、二兩種方法比較簡單粗暴,三、四兩種方法是比較常見的。
縱觀目前主流的圖片載入庫:Picasso,Glide,Fresco,Android-Universal-Image-Loader等,它們都可以顯示圓角的圖片,但是除了Fresco可以指定哪幾個角為圓角、哪幾個角不為圓角外,其他三者都沒有實現這個功能,而Fresco的侵入性比較強,佈局中需使用com.facebook.drawee.view.simpledraweeview
本文基於大眾化圖片載入庫Universal-Image-Loader中的RoundedBitmapDisplayer原始碼進行拓展,自定義了一個FlexibleRoundedBitmapDisplayer,可以用簡單快捷的程式碼指定圖片的某幾個角為圓角,某幾個角不為圓角。
首先我們看一下Universal-Image-Loader中的RoundedBitmapDisplayer原始碼:
public class RoundedBitmapDisplayer implements BitmapDisplayer {
protected final int cornerRadius;
protected final int margin;
public RoundedBitmapDisplayer(int cornerRadiusPixels) {
this(cornerRadiusPixels, 0);
}
public RoundedBitmapDisplayer(int cornerRadiusPixels, int marginPixels) {
this.cornerRadius = cornerRadiusPixels;
this .margin = marginPixels;
}
public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
if(!(imageAware instanceof ImageViewAware)) {
throw new IllegalArgumentException("ImageAware should wrap ImageView. ImageViewAware is expected.");
} else {
imageAware.setImageDrawable(new RoundedBitmapDisplayer.RoundedDrawable(bitmap, this.cornerRadius, this.margin));
}
}
public static class RoundedDrawable extends Drawable {
protected final float cornerRadius;
protected final int margin;
protected final RectF mRect = new RectF();
protected final RectF mBitmapRect;
protected final BitmapShader bitmapShader;
protected final Paint paint;
public RoundedDrawable(Bitmap bitmap, int cornerRadius, int margin) {
this.cornerRadius = (float)cornerRadius;
this.margin = margin;
this.bitmapShader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);
this.mBitmapRect = new RectF((float)margin, (float)margin, (float)(bitmap.getWidth() - margin), (float)(bitmap.getHeight() - margin));
this.paint = new Paint();
this.paint.setAntiAlias(true);
this.paint.setShader(this.bitmapShader);
this.paint.setFilterBitmap(true);
this.paint.setDither(true);
}
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
this.mRect.set((float)this.margin, (float)this.margin, (float)(bounds.width() - this.margin), (float)(bounds.height() - this.margin));
Matrix shaderMatrix = new Matrix();
shaderMatrix.setRectToRect(this.mBitmapRect, this.mRect, ScaleToFit.FILL);
this.bitmapShader.setLocalMatrix(shaderMatrix);
}
public void draw(Canvas canvas) {
canvas.drawRoundRect(this.mRect, this.cornerRadius, this.cornerRadius, this.paint);
}
public int getOpacity() {
return -3;
}
public void setAlpha(int alpha) {
this.paint.setAlpha(alpha);
}
public void setColorFilter(ColorFilter cf) {
this.paint.setColorFilter(cf);
}
}
}
可以看出Universal-Image-Loader顯示圓角圖片的方法屬於上述第四種方法——BitmapShader,Shader被稱之為著色器、渲染器,而BitmapShader產生的是一個影象,有點類似Photoshop中的影象漸變填充,它的作用就是通過Paint對畫布進行指定Bitmap的填充,這裡的填充模式是TileMode.CLAMP(拉伸模式,拉伸圖片最後一畫素點,一般指定圖片合適大小時不會拉伸),也就是使用BitmapShader來進行圖形填充。通俗些說就是用一張圖片建立了一支具有影象填充功能的畫筆,然後使用這個畫筆畫出一個形狀,圖片就在畫出的地方顯示。Universal-Image-Loader這裡用drawRoundRect()方法畫出了一個圓角矩形,故影象顯示成了圓角矩形。
用過Photoshop的童鞋應該比較好理解,這就類似你給一個圖層加了一個白蒙板,然後用不透明度100%的黑色畫筆去在蒙板上繪製,畫筆所過之處你原來的圖層就會顯現出來,就是這個意思。
那RoundedBitmapDisplayer畫圓角圖片的原理分析完了,說一下實現指定某幾個角為圓角的思路:
思路就是先畫一個圓角矩形把這個圖片變成圓角,然後你想讓那個角不是圓角,就把對應角位置那部分的原圖畫出來即可,畫一個矩形就可以把原來的角顯示出來,用的方法是drawRect()。
下面就是FlexibleRoundedBitmapDisplayer的完整程式碼:
/**
* Universal-Image-Loader中RoundedBitmapDisplayer的增強版,可以自定義圖片4個角中的指定角為圓角<br>
*/
public class FlexibleRoundedBitmapDisplayer implements BitmapDisplayer {
protected int cornerRadius;
protected int corners;
public static final int CORNER_TOP_LEFT = 1;
public static final int CORNER_TOP_RIGHT = 1 << 1;
public static final int CORNER_BOTTOM_LEFT = 1 << 2;
public static final int CORNER_BOTTOM_RIGHT = 1 << 3;
public static final int CORNER_ALL = CORNER_TOP_LEFT | CORNER_TOP_RIGHT | CORNER_BOTTOM_LEFT | CORNER_BOTTOM_RIGHT;
/**
* 構造方法說明:設定圓角畫素大小,所有角都為圓角
* @param cornerRadiusPixels 圓角畫素大小
*/
public FlexibleRoundedBitmapDisplayer(int cornerRadiusPixels){
this.cornerRadius = cornerRadiusPixels;
this.corners = CORNER_ALL;
}
/**
* 構造方法說明:設定圓角畫素大小,指定角為圓角
* @param cornerRadiusPixels 圓角畫素大小
* @param corners 自定義圓角<br>
* CORNER_NONE 無圓角<br>
* CORNER_ALL 全為圓角<br>
* CORNER_TOP_LEFT | CORNER_TOP_RIGHT | CORNER_BOTTOM_LEFT | CORNER_BOTTOM_RIGHT 指定圓角(選其中若干組合 ) <br>
*/
public FlexibleRoundedBitmapDisplayer(int cornerRadiusPixels, int corners){
this.cornerRadius = cornerRadiusPixels;
this.corners = corners;
}
@Override
public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
if (!(imageAware instanceof ImageViewAware)) {
throw new IllegalArgumentException("ImageAware should wrap ImageView. ImageViewAware is expected.");
}
imageAware.setImageDrawable(new FlexibleRoundedDrawable(bitmap,cornerRadius,corners));
}
public static class FlexibleRoundedDrawable extends Drawable {
protected final float cornerRadius;
protected final RectF mRect = new RectF(), mBitmapRect;
protected final BitmapShader bitmapShader;
protected final Paint paint;
private int corners;
public FlexibleRoundedDrawable(Bitmap bitmap, int cornerRadius, int corners) {
this.cornerRadius = cornerRadius;
this.corners = corners;
bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mBitmapRect = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(bitmapShader);
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mRect.set(0, 0, bounds.width(), bounds.height());
Matrix shaderMatrix = new Matrix();
shaderMatrix.setRectToRect(mBitmapRect, mRect, Matrix.ScaleToFit.FILL);
bitmapShader.setLocalMatrix(shaderMatrix);
}
@Override
public void draw(Canvas canvas) {
//先畫一個圓角矩形將圖片顯示為圓角
canvas.drawRoundRect(mRect, cornerRadius, cornerRadius, paint);
int notRoundedCorners = corners ^ CORNER_ALL;
//哪個角不是圓角我再把你用矩形畫出來
if ((notRoundedCorners & CORNER_TOP_LEFT) != 0) {
canvas.drawRect(0, 0, cornerRadius, cornerRadius, paint);
}
if ((notRoundedCorners & CORNER_TOP_RIGHT) != 0) {
canvas.drawRect(mRect.right - cornerRadius, 0, mRect.right, cornerRadius, paint);
}
if ((notRoundedCorners & CORNER_BOTTOM_LEFT) != 0) {
canvas.drawRect(0, mRect.bottom - cornerRadius, cornerRadius, mRect.bottom, paint);
}
if ((notRoundedCorners & CORNER_BOTTOM_RIGHT) != 0) {
canvas.drawRect(mRect.right - cornerRadius, mRect.bottom - cornerRadius, mRect.right, mRect.bottom, paint);
}
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Override
public void setAlpha(int alpha) {
paint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
paint.setColorFilter(cf);
}
}
}
然後就可以這樣使用Universal-Image-Loader了:
/*
* 設定圖片---自定義圖片4個角中的指定角為圓角
* @param url 圖片的url
* @param cornerRadius 圓角畫素大小
* @param corners 自定義圓角:<br>
* 以下引數為FlexibleRoundedBitmapDisplayer中靜態變數:<br>
* CORNER_NONE 無圓角<br>
* CORNER_ALL 全為圓角<br>
* CORNER_TOP_LEFT | CORNER_TOP_RIGHT | CORNER_BOTTOM_LEFT | CORNER_BOTTOM_RIGHT 指定圓角(選其中若干組合 ) <br>
* @param image url為空時載入該圖片
* @param imageView 要設定圖片的ImageView
*/
public void setRoundedImage(String url, int cornerRadius, int corners, int image, ImageView imageView) {
ImageLoader imageLoader = ImageLoader.getInstance();
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(image).showStubImage(image)
.showImageForEmptyUri(image)//url為空時顯示的圖片
.showImageOnFail(image)//載入失敗顯示的圖片
.cacheInMemory()//記憶體快取
.cacheOnDisc()//磁碟快取
.displayer(new FlexibleRoundedBitmapDisplayer(cornerRadius,corners)) // 自定義增強型BitmapDisplayer
.build();
imageLoader.displayImage(url, imageView, options);
}
來個示例:
看看效果: