通过前面两篇的学习,总算对Android-Universal-Image-Loader
有些熟悉了。还没有看前面两篇的小伙伴们赶快去看看吧:Android-Universal-Image-Loader源码分析、Android-Universal-Image-Loader缓存策略,咋们还是从Android-Universal-Image-Loader
的demo看他的效果图:
这里demo中通过builder中的displayer方法中传入了一个CircleBitmapDisplayer
对象,最后设置给了DisplayImageOptions
成员变量displayer
。那最终该displayer
用在了什么地方呢?那咱们还是顺着源码去看下ImageLoader
中displayImage
方法最后调用了这么一句:
options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
那咱们还是直接去刚才传的CircleBitmapDisplayer
中display
方法吧:
@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 CircleDrawable(bitmap, strokeColor, strokeWidth));
}
上面是通过ImageAware
的setImageDrawable
方法传了一个CircleDrawable
对象,看名字都知道,其实CircleDrawable
是一个Drawable
对象,咱们此处的imageAware
是一个ImageViewAware
类型的对象,那咱们看看它的setImageDrawable
方法是怎么完成显示的,该方法在父类ViewAware
中实现的方法:
@Override
public boolean setImageDrawable(Drawable drawable) {
if (Looper.myLooper() == Looper.getMainLooper()) {
View view = viewRef.get();
if (view != null) {
setImageDrawableInto(drawable, view);
return true;
}
} else {
L.w(WARN_CANT_SET_DRAWABLE);
}
return false;
}
紧接着又调用了setImageDrawableInto
方法,将传进来的drawable
和当前的view
作为参数:
/**
* Should set drawable into incoming view. Incoming view is guaranteed not null.<br />
* This method is called on UI thread.
*/
protected abstract void setImageDrawableInto(Drawable drawable, View view);
好吧,转来转去还是要去子类看看如何设置这个drawable
了,还是回到ImageViewAware
去看看吧:
@Override
protected void setImageDrawableInto(Drawable drawable, View view) {
//子类中的view一定是ImageView,所以直接强转了
((ImageView) view).setImageDrawable(drawable);
//如果是有动画的drawable才走这里
if (drawable instanceof AnimationDrawable) {
((AnimationDrawable)drawable).start();
}
}
oh,my god!!!就这么简单的一句啊,说白了这里就是定义一个圆形,带外框的drawable了,下面着重就是看下CircleBitmapDisplayer
中内部类CircleDrawable
:
public static class CircleDrawable extends Drawable {
protected float radius;
protected final RectF mRect = new RectF();
protected final RectF mBitmapRect;
protected final BitmapShader bitmapShader;
protected final Paint paint;
protected final Paint strokePaint;
protected final float strokeWidth;
protected float strokeRadius;
public CircleDrawable(Bitmap bitmap, Integer strokeColor, float strokeWidth) {
radius = Math.min(bitmap.getWidth(), bitmap.getHeight()) / 2;
//将当前的bitmap放到bitmapShader上,用于后面画笔画出来
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);
//设置了bitmapShader后,说明该画笔画的是bitmap对象了
paint.setShader(bitmapShader);
paint.setFilterBitmap(true);
paint.setDither(true);
if (strokeColor == null) {
strokePaint = null;
} else {
strokePaint = new Paint();
strokePaint.setStyle(Paint.Style.STROKE);
strokePaint.setColor(strokeColor);
strokePaint.setStrokeWidth(strokeWidth);
strokePaint.setAntiAlias(true);
}
this.strokeWidth = strokeWidth;
strokeRadius = radius - strokeWidth / 2;
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
//获取到view占据的位置
mRect.set(0, 0, bounds.width(), bounds.height());
radius = Math.min(bounds.width(), bounds.height()) / 2;
//方框的半径要小点
strokeRadius = radius - strokeWidth / 2;
// Resize the original bitmap to fit the new bound
Matrix shaderMatrix = new Matrix();
//重新设置图片的matrix,让图片大小适应view的大小
shaderMatrix.setRectToRect(mBitmapRect, mRect, Matrix.ScaleToFit.FILL);
bitmapShader.setLocalMatrix(shaderMatrix);
}
@Override
public void draw(Canvas canvas) {
//先要画bitmap对象
canvas.drawCircle(radius, radius, radius, paint);
if (strokePaint != null) {
//方框放在上面
canvas.drawCircle(radius, radius, strokeRadius, strokePaint);
}
}
@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);
}
}
上面画圆形的bitmap用到了paint的BitmapShader方式,这里也介绍下其他的方式实现,用paint的setXfermode来过滤也可以实现,用canvas的裁剪也可以实现。但是实现之前需要将bitmap适应当前view(放大或缩小)。
好了,图片转换就介绍到这里。期待最后一篇自定义Android-Universal-Image-Loader
缓存和图片转换
网友评论