本文GitHub項目地址 : 統一的圖片加載架構html
咱們目前的項目對於圖片加載的需求很大,一直以來,咱們使用的是Glide做爲圖片加載的底層包,爲了節省圖片佔用的空間,但願使用webp的格式來展現動圖。因爲Glide不支持Animated WebP(即WebP動圖) 格式,因此咱們須要把底層的Glide替換爲Fresco。相信用過這兩個包的同窗都知道,二者的差別仍是比較大的,想要保證在不大量修改代碼,也不入侵業務代碼的前提下遷移到Fresco上,是個值得思考的問題。java
因爲以前已經把項目的圖片加載模塊重構整合了一次,把圖片加載和業務代碼有效分離開,所以此次遷移也算有一個較好的基礎。至於整合的具體思路以及爲何整合這些模塊的意義,我在《封裝並實現統一的圖片加載架構》文章裏面已經講了不少,沒看過那篇文章的同窗建議看看,由於接下來就是在這篇文章的基礎上去分析如何遷移到Fresco上的問題。git
咱們都知道,Glide是使用了ImageView來加載圖片的,而實際項目中還會有咱們自定義的ImageView,好比CircleImageView等,而Fresco則是經過DraweeView來加載圖片的,這是一個很是嚴重的問題。github
因此咱們須要考慮的是在項目遷移到Fresco上的時候,究竟是想辦法沿用ImageView來加載圖片,仍是想辦法使用SimpleDraweeView來加載圖片,若是是前者,那麼咱們應該怎麼作?假如是後者,要怎樣才能保證Fresco的代碼不會大量入侵到業務代碼中,同時又能兼容原來項目的接口呢?web
Tips : 對於Fresco本身的圖片加載容器DraweeView,目前暫時繼承自ImageView,可是官方表示將來會直接繼承自View,咱們開發時經常使用DraweeView的子類SimpleDraweeView來加載圖片。緩存
優勢bash
在我看官方文檔的時候,暗喜了良久。由於若是能夠這麼作的話,那麼這就是最簡單的直接的遷移到Fresco上的方法。架構
缺陷:框架
結論:ide
具體操做大概就是當調用了以下加載接口的時候,把ImageView動態替換成SimpleDraweeView,而後加載圖片
void showImage(ImageLoaderOptions options); // ImageLoaderOptions包含ImageView,Url 等等複製代碼
上面四個方案是我所想到的全部的實現方案,基本上方案三和方案四實現後的效果是最佳的。可是方案三的難度更大,因此綜合來看,方案四的性價比更好。(目前項目使用的就是方案四,效果良好,運行正常)
肯定了方案,咱們能夠開始實踐了,結合方案四的思路,代碼實現上基本上是沒有什麼難度了。
對於如何整合圖片加載模塊,請務必參考以前的文章《封裝並實現統一的圖片加載架構》
Fresco加載模塊的重點代碼實現以下:
FrescoImageLoader.java
// 項目種幾乎全部的圖片加載都調用到了這裏
@Override
public void showImage(@NonNull ImageLoaderOptions options) {
showImgaeDrawee(options);
}
private void showImgaeDrawee(ImageLoaderOptions options) {
// 這個View就是加載圖片的ImageView
View view=options.getViewContainer();
SimpleDraweeView drawee=null;
Class clazz=null;
GenericDraweeHierarchy hierarchy=null;
GenericDraweeHierarchyBuilder hierarchyBuilder = GenericDraweeHierarchyBuilder.newInstance(getResources());
// 因爲本身的項目中有好幾種ImageView,所以須要一一判斷
if (view instanceof SquareRImageView) {
clazz= SquareRImageView.class;
drawee=getDraweeView(view,clazz);
if (drawee != null) {
drawee.setAspectRatio(1);
}
}else if (view instanceof CircleImageView){
clazz= CircleImageView.class;
// 傳入
drawee=getDraweeView(view,clazz);
hierarchyBuilder.setFadeDuration(400).setRoundingParams(RoundingParams.asCircle());
}else if (view instanceof SimpleDraweeView){
drawee= (SimpleDraweeView) view;
hierarchy=drawee.getHierarchy();
}else if(view instanceof ImageView){
clazz= ImageView.class;
drawee=getDraweeView(view,clazz);
}
else {
Logger.i("no type !!");
return;
}
if (drawee != null) {
// 圖片地址
Uri uri=Uri.parse(options.getUrl());
if (options.getHolderDrawable()!=-1) {
hierarchyBuilder.setPlaceholderImage(options.getHolderDrawable());
}
if (options.getErrorDrawable()!=-1) {
hierarchyBuilder.setFailureImage(options.getErrorDrawable());
}
if (hierarchy == null) {
hierarchy= hierarchyBuilder.build();
}
drawee.setHierarchy(hierarchy);
PipelineDraweeControllerBuilder controllerBuilder=Fresco.newDraweeControllerBuilder().setUri(uri).setAutoPlayAnimations(true);
ImageRequestBuilder imageRequestBuilder= ImageRequestBuilder.newBuilderWithSource(uri);
if (options.getImageSize() != null) {
imageRequestBuilder.setResizeOptions(new ResizeOptions(getSize(options.getImageSize().getWidth(),view), getSize(options.getImageSize().getWidth(),view)));
}
if (options.isBlurImage()) {
// 是否作高斯模糊
imageRequestBuilder.setPostprocessor(new BlurPostprocessor(view.getContext().getApplicationContext(), 15));
}
ImageRequest request =imageRequestBuilder.build();
controllerBuilder.setImageRequest(request);
DraweeController controller=controllerBuilder.build();
drawee.setController(controller);
}
}複製代碼
在圖片加載時,首先須要判斷加載圖片的容器是ImageView仍是ImageView的子類,由於這意味着對圖片不一樣的處理,好比CircleImageView意味着是加載一個圓圖,因此咱們須要設置SimpleDraweeView爲圓圖等等。
// 傳入加載圖片的ImageView,返回一個相同位置,相同大小的SimpleDraweeView
private SimpleDraweeView getDraweeView(View viewContainer,Class<?> classType) {
if (viewContainer instanceof SimpleDraweeView){
return (SimpleDraweeView) viewContainer;
}
SimpleDraweeView mDraweeView=null;
if (classType.isInstance(viewContainer)){
FrameLayout layout=new FrameLayout(viewContainer.getContext());
if(viewContainer.getParent() instanceof FrameLayout){
FrameLayout parent= (FrameLayout) viewContainer.getParent();
FrameLayout.LayoutParams params= (FrameLayout.LayoutParams) viewContainer.getLayoutParams();
// 這個方法來完成最終的添加
mDraweeView=exchangeChilde(parent,viewContainer,params);
}else if(viewContainer.getParent() instanceof RelativeLayout){
RelativeLayout parent= (RelativeLayout) viewContainer.getParent();
RelativeLayout.LayoutParams params= (RelativeLayout.LayoutParams) viewContainer.getLayoutParams();
mDraweeView=exchangeChilde(parent,viewContainer,params);
}else if(viewContainer.getParent() instanceof LinearLayout){
// 當ImageView 的Parent時LinearLayout的時候,處理會有一些不一樣
LinearLayout parent= (LinearLayout) viewContainer.getParent();
LinearLayout.LayoutParams params= (LinearLayout.LayoutParams) viewContainer.getLayoutParams();
layout.setLayoutParams(params);
addToViewGroup(parent,viewContainer,layout);
layout.addView(viewContainer);
mDraweeView=exchangeChilde(layout,viewContainer,params);
}else{
//基本上能夠涵蓋上面一個項目中用到的佈局類型了,
//其餘的類型如Tablayout等等,視實際狀況而定
ViewParent parent=viewContainer.getParent();
Logger.i("");
}
}else{
Logger.i("");
}
return mDraweeView;
}
// 該方將ImageView從原來的Parent種移除,並添加到一個FrameLayout中去
private void addToViewGroup(ViewGroup parent,View viewOld,View viewNew){
for (int i = 0; i < parent.getChildCount(); i++) {
if (parent.getChildAt(i).equals(viewOld)) {
parent.removeView(viewOld);
parent.addView(viewNew,i);
return;
}
}
}複製代碼
這裏須要判斷ImageView的父容器ViewGroup是那些,須要着重區分LinearLayout這個父佈局,由於若是ImageView的父容器是LinearLayout,那麼咱們就沒法在LinearLayout中添加一個大小相同,位置和ImageView重合的SimpleDraweeView來加載圖片了,所以,此時咱們須要把這個ImageView拿出來,把它和SimpleDraweeView一塊兒裝在FrameLayout中,而後在把FrameLayout添加到ImageView原來在LinearLayout中所處的位置。
// 緊挨着ImageView添加SimpleDraweeView到原來的ImageView的位置
private SimpleDraweeView exchangeChilde(ViewGroup parent, View testImageView, ViewGroup.LayoutParams layoutParams) {
SimpleDraweeView draweeview =null;
for (int i = 0; i < parent.getChildCount(); i++) {
if (testImageView.equals(parent.getChildAt(i))) {
if (testImageView instanceof ImageView) {
ImageView img= (ImageView) testImageView;
img.setBackgroundDrawable(null);
img.setImageDrawable(null);
}
if (i+1 < parent.getChildCount()) {
View child=parent.getChildAt(i+1);
// 此處理應作更加仔細的判斷
if (child instanceof SimpleDraweeView) {
return (SimpleDraweeView) child;
}
}
draweeview=new SimpleDraweeView(testImageView.getContext());
draweeview.setLayoutParams(layoutParams);
parent.addView(draweeview,i+1);
return draweeview;
}
}
return draweeview;
}複製代碼
以上基本上就是以Fresco來實現圖片加載模塊的核心代碼了,基本能夠覆蓋原有的Glide的功能,而且入侵度低,無需修改原有代碼,隨時可替換。
暫無
項目已經上傳了github,點此獲取,求star! 求follow !