Fresco图片显示原理浅析
(第一篇)Fresco架构设计赏析
(第二篇)Fresco缓存架构分析
本文是Fresco源码分析系列第三篇文章,主要来分析一下
Fresco UI
层的实现,包括下面这些点:
- 图片显示原理,图片加载过程中各个阶段的图片切换原理。(比方由占位图->目标图片)
- 圆角的实现
- ScaleType的实现
图片显示原理与多状态切换逻辑
在Fresco
中负责图片展现工作的是DraweeHierarchy
,它内部维护着一个Drawable
序列,在图片加载过程中的不同阶段可以显示不同状态的Drawable
。
图片显示原理
我们一般直接使用SimpleDraweeView
,它继承自DraweeView
:
DraweeView.java
public class DraweeView<DH extends DraweeHierarchy> extends ImageView { public void setController(@Nullable DraweeController draweeController) { mDraweeHolder.setController(draweeController); super.setImageDrawable(mDraweeHolder.getTopLevelDrawable()); }}
在调用SimpleDraweeView.setImageUri()
时会调用到DraweeView.setController()
,即此时是直接显示的mDraweeHolder.getTopLevelDrawable()
:
DraweeHolder.java
public @Nullable Drawable getTopLevelDrawable() { return mHierarchy == null ? null : mHierarchy.getTopLevelDrawable();}
所以最终的显示的Drawable
是mHierarchy.getTopLevelDrawable()
。mHierarchy
的实现是GenericDraweeHierarchy
。mHierarchy.getTopLevelDrawable()
获取的Drawable
实际上可以了解为FadeDrawable
:
GenericDraweeHierarchy.java
GenericDraweeHierarchy(GenericDraweeHierarchyBuilder builder) { mFadeDrawable = new FadeDrawable(layers); Drawable maybeRoundedDrawable = WrappingUtils.maybeWrapWithRoundedOverlayColor(mFadeDrawable, mRoundingParams); mTopLevelDrawable = new RootDrawable(maybeRoundedDrawable); //RootDrawable 只是一个装饰类 }
FadeDrawable
内部维护着一个Drawable
数组,它可以由一个Drawable
切换到另一个Drawable
,Drawable
的切换过程中伴有着透明度改变的动画:
public class FadeDrawable extends ArrayDrawable { private final Drawable[] mLayers; @Override public void draw(Canvas canvas) { ...升级Drawable的透明度 //从前往后一层一层的画出来 for (int i = 0; i < mLayers.length; i++) { drawDrawableWithAlpha(canvas, mLayers[i], mAlphas[i] * mAlpha / 255); } }}
那Fresco
中一共有多少层Drawable(layer)
呢?我们看一下GenericDraweeHierarchy
的初始化代码:
GenericDraweeHierarchy.java
private static final int BACKGROUND_IMAGE_INDEX = 0; private static final int PLACEHOLDER_IMAGE_INDEX = 1; private static final int ACTUAL_IMAGE_INDEX = 2; private static final int PROGRESS_BAR_IMAGE_INDEX = 3; private static final int RETRY_IMAGE_INDEX = 4; private static final int FAILURE_IMAGE_INDEX = 5; private static final int OVERLAY_IMAGES_INDEX = 6; GenericDraweeHierarchy(GenericDraweeHierarchyBuilder builder) { Drawable[] layers = new Drawable[numLayers]; // 一般是6层 layers[BACKGROUND_IMAGE_INDEX] = ... layers[PLACEHOLDER_IMAGE_INDEX] = ... layers[ACTUAL_IMAGE_INDEX] = ... layers[PROGRESS_BAR_IMAGE_INDEX] = ... layers[RETRY_IMAGE_INDEX] = ... layers[FAILURE_IMAGE_INDEX] = ... ...这里还有一个overlayer层 }
即在构造GenericDraweeHierarchy
就确定了有几层Drawable
(Fresco
中numLayers
的值一般为6
)。当然假如没有这一层Drawable
(比方没有提供Progress Drawable
),那么这一层Drawable
就是null。通过FadeDrawable.draw()
已经知道会按照顺序把这些Drawable
都画出来(Drawable为null的话就不会画, 透明度为0也不会画)。
可以看到我们实际上要显示的图片位于第3层级。那么假如图片加载完成,如何从加载进度的Drawable
切换到实际的图片呢?:
GenericDraweeHierarchy.java
public void setImage(Drawable drawable, float progress, boolean immediate) { drawable = WrappingUtils.maybeApplyLeafRounding(drawable, mRoundingParams, mResources); //包裹上圆形参数 ... fadeOutBranches(); fadeInLayer(ACTUAL_IMAGE_INDEX); ...}private void fadeOutBranches() { fadeOutLayer(PLACEHOLDER_IMAGE_INDEX); fadeOutLayer(ACTUAL_IMAGE_INDEX); fadeOutLayer(PROGRESS_BAR_IMAGE_INDEX); fadeOutLayer(RETRY_IMAGE_INDEX); fadeOutLayer(FAILURE_IMAGE_INDEX);}
当加载完成完最终图片后就会调用到GenericDraweeHierarchy.setImage()
,上面逻辑其实涉及到的代码很多,但是逻辑很简单就不深入看了。上面的两个核心方法可以这样了解:
- fadeOutLayer() : 把这一层Drawable(layer)的透明度设置为0
- fadeInLayer() : 把这一层的透明度设置为1
到这里,基本上就叙述了Fresco
的图片显示原理。其实整体流程可以用下图表示:
Fresco图片显示原理.png
圆角的实现
直接来看具体的实现代码:
WrappingUtils.java
private static Drawable applyLeafRounding(Drawable drawable, RoundingParams roundingParams, Resources resources) { if (drawable instanceof BitmapDrawable) { final BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; RoundedBitmapDrawable roundedBitmapDrawable = new RoundedBitmapDrawable(resources,bitmapDrawable.getBitmap(),bitmapDrawable.getPaint()); applyRoundingParams(roundedBitmapDrawable, roundingParams); return roundedBitmapDrawable; } ... return drawable;}
RoundedBitmapDrawable.java
@Override public void draw(Canvas canvas) { if (!shouldRound()) { super.draw(canvas); return; } ... updatePath(); //升级圆角path or 圆形path updatePaint(); ... canvas.drawPath(mPath, mPaint); } private void updatePaint() { if (mLastBitmap == null || mLastBitmap.get() != mBitmap) { mLastBitmap = new WeakReference<>(mBitmap); mPaint.setShader(new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)); mIsShaderTransformDirty = true; } ... }
即Fresco
的圆角实际上是使用BitmapShader
来实现的。
ScaleType的实现
Fresco
的ScaleType
的实现原理其实和ImageView
是相同的。但因为SimpleDraweeView
内部是维护了多个Drawable
,所以它并不能直接使用ImageView
的实现方式,它需要把它维护的每一个Drawable
都做对应的ScaleType
操作。我们先来看一下ImageView
的ScaleType
的实现:
ImageView ScaleType的实现
ImageView中 CENTER_CROP 的实现
private void configureBounds() { //drawable 的宽高 final int dwidth = mDrawableWidth; final int dheight = mDrawableHeight; //当前view的宽高 final int vwidth = getWidth() - mPaddingLeft - mPaddingRight; final int vheight = getHeight() - mPaddingTop - mPaddingBottom; ... 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)); // 从哪个坐标开始画 Drawable } ... }
Fresco的实现
在Fresco
中假如对图片设置了ScaleType
,那么就会把对应的Drawable
封装为ScaleTypeDrawable
, 它的draw()
:
public void draw(Canvas canvas) { // 这个方法相似于 ImageView configureBounds的实现, 配置了 mDrawMatrix configureBoundsIfUnderlyingChanged(); if (mDrawMatrix != null) { int saveCount = canvas.save(); canvas.clipRect(getBounds()); canvas.concat(mDrawMatrix); super.draw(canvas); canvas.restoreToCount(saveCount); } else { super.draw(canvas); } }
欢迎关注我的Android进阶计划看更多干货
欢迎关注我的微信公众号:susion随心
微信公众号.jpeg
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Fresco图片显示原理浅析