Android图片加载框架的封装(Glide封装为例)

  • 前言

       接手之前的项目,发现图片加载用的是glide,但是每处加载的地方都把GlideApp.with()一系列配置写下来,虽然代码也不长,但是这样一来显得不优雅,二来重复代码太多,三来可维护性也差,所以当时就简单地写了个图片加载的封装类,单例对外提供static方法,虽说一定程度上规避了代码重复,但是一个app里图片的样式好几种,每加一个又得写一个对外方法,久而久之,这个封装类必定变得越来越臃肿;再者,如果某些特殊需求需要切换另外的图片加载框架,那也是挺灾难的情况。所以本篇文章尝试解决这样的问题,在此之前我看到有人写了快速开发框架MVPArms,看到里面图片加载的封装就挺好的,学习了下,在这里做下梳理。以下是大概的封装思路:

封装思路.png

  • 实战

       如上文所说,目前的封装一个缺点是,图片样式多种,每增加一种样式,都要在ImageLoader里增加一个方法,在方法里更改配置,比如placeHolder,transition等,所以首先考虑的就是把配置分割出去,单独写一个类,外部自由定制,如果配置项比较多,这时候就可以用上构建者模式,更好的做法也可以写过基类,因为每个图片的加载,肯定涉及到图片的url,图片加载的目标对象(比如ImageView)等,这里就可以提到一个基类里:

public class ImageConfig {
    protected String url;
    protected ImageView imageView;
    protected int placeholder;//占位符

    public String getUrl() {
        return url;
    }

    public ImageView getImageView() {
        return imageView;
    }

    public int getPlaceholder() {
        return placeholder;
    }
}

       不同的图片加载框架,不同的配置类,就可以继承ImageConfig进行扩展,比如图片的圆角,模糊度,背景色,(顺便贴上强大的样式扩展库glide-transformations,支持多种样式)

public class ImageConfigImpl extends ImageConfig {
    private int imageRadius;//图片每个圆角的大小
    private int blurValue;//高斯模糊值, 值越大模糊效果越大
  
   //建造者模式来构建配置信息这个对象
    private ImageConfigImpl(Builder builder) {
        this.url = builder.url;
        this.imageView = builder.imageView;
        this.placeholder = builder.placeholder;     
        this.imageRadius = builder.imageRadius;
        this.blurValue = builder.blurValue;
    }

     public int getBlurValue() {
        return blurValue;
    }

     public int getImageRadius() {
        return imageRadius;
    }

     public static Builder builder() {
        return new Builder();
    }

    public static final class Builder {
        private String url;
        private ImageView imageView;
        private int imageRadius;//图片每个圆角的大小
        private int blurValue;//高斯模糊值, 值越大模糊效果越大

        private Builder() {
        }

        public Builder url(String url) {
            this.url = url;
            return this;
        }

        public Builder placeholder(int placeholder) {
            this.placeholder = placeholder;
            return this;
        }

        public Builder imageView(ImageView imageView) {
            this.imageView = imageView;
            return this;
        }

        public Builder imageRadius(int imageRadius) {
            this.imageRadius = imageRadius;
            return this;
        }

        public Builder blurValue(int blurValue) { //blurValue 建议设置为 15
            this.blurValue = blurValue;
            return this;
        }
       //构造出ImageConfigImpl对象
        public ImageConfigImpl build() {
            return new ImageConfigImpl(this);
        }
}

       我们使用到了构建者模式,它有个好处就是,即使你因为需求增加一种样式,直接到Build里添加相应的属性,而不会影响到其它使用方,因为每个使用方都是自己定制这个Buider对象,互不影响。到这边,我们对图片的配置信息已经写好,外部可以进行个性化定制,比如

ImageConfigImpl.builder()
                        .url(data.getAvatarUrl())
                        .imageView(mAvatar)
                        .build()

       这样不同配置还要追加一个方法的缺点已经被我们用构建者模式解决;接下来就是:如果切换了其他的图片加载库怎么办(当然这种情况还是比较少的)
       当我们碰到一个功能,可能会有很多种算法的实现,又想做随时的切换,这时候可以用上策略模式,顾名思义,策略模式就是根据不同的场景采取不同的策略来完成需求。
       首先,我们刚才说碰到一个“功能”,那么先把这个功能写一个接口抽象起来,要求各个策略者实现各自具体的加载方法。

public interface BaseImageLoaderStrategy {
  //定义供外部调用的显示图片的方法
    void displayImage(Context contex, T config);
}

       这里我们发现,接口带有继承于ImageConfig的泛型,这个作用也很明显,就是将外部自己构建的ImageConfig实例传给具体的displayImage实现方法,才能拿到你配置的config,比如下面的displayImage实现方法

  • 以Glide为例
public class GlideImageLoaderStrategy implements BaseImageLoaderStrategy {

    @Override
    public void displayImage(Context context, ImageConfigImpl config) {
         //这里实现Glide图片加载方法,也就是GlideApp.with(context)...那一串
        //config中拿出用户配置的参数 如 config.getBlurValue()
    }
}
  • Picasso为例
public class PicassoImageLoaderStrategy implements BaseImageLoaderStrategy {

    @Override
    public void displayImage(Context contex, PicassoConfigImpl config) {
         //这里实现Picasso图片加载方法   Picasso.with(context)....那一串
        //PicassoConfigImpl 与ImageConfigImpl 一个意思,省略不写
    }
}

       策略实现类我们已经写好了,现在就是对外暴露我们的图片显示策略,写一个统一类:ImageLoader

public final class ImageLoader {

     private BaseImageLoaderStrategy mStrategy;
     /**
     * 加载图片
     */
    public  void loadImage(Context context,T config) {
        if(this.mStrategy==null){
             throw new 
             NullPointerException("you should invoke setLoadImgStrategy first");
    }
        this.mStrategy.displayImage(context,config);
    }

   //设置上你的策略实现类对象,让它去调自己的displayImage()方法
    public void setLoadImgStrategy(BaseImageLoaderStrategy strategy) {
        this.mStrategy = strategy;
    }

    public BaseImageLoaderStrategy getLoadImgStrategy() {
        return mStrategy;
    }
}

       外部需要显示图片的,都只需要调用ImageLoader的disolayImage方法,注意,必须先注入自己的代理类实例,比如你的GlideImageLoaderStrategy或PicassoImageLoaderStrategy 等,我们来看下调用方是怎么调用的:

mImageLoader.loadImage(itemView.getContext(),
                ImageConfigImpl
                        .builder()
                        .url(data.getAvatarUrl())
                        .imageView(mAvatar)
                        .build());
  • 总结

       以上就是图片加载框架的封装思路,通过构建者模式优化了图片加载配置项的管理,并且调用方构建自己的对象互不干扰;通过策略模式也规避了切换图片框架的风险;这就是java设计模式给代码维护带来的便利,当然,学以致用,才能得心应手。

你可能感兴趣的:(Android图片加载框架的封装(Glide封装为例))