美文网首页AndroidAndroid 框架Android Dev
使用Glide加载图片系列之一从不同的数据源加载图片

使用Glide加载图片系列之一从不同的数据源加载图片

作者: 倾城_之泪 | 来源:发表于2015-11-01 13:39 被阅读59172次

    与其他图片加载库相同,Glide除了可以加载网络图片之外,也可以加载本地图片。甚至还可以从各种各样奇葩的数据源中加载图片。

    加载网络图片

    很多情况下,我们使用图片加载库就是为了加载网络图片。网络操作是一个很复杂的东西。试想一下,如果没有图片加载库,我们就要手动去下载图片,缓存图片,最后再从文件里面读取bitmap并设置到Imageview里面。这还算好的,要是在Listview里面你会更头疼的。原因我就不说了,你懂的~~再加上各种各样的Bitmap操作,保准你再也不想撸代码了。而且Bitmap这东西还很占内存,伺候不好,很容易就会引发OOM,app吧唧就闪退了!!

    图片加载库的优势就在于此。简简单单一句话,下载,缓存,加载统统搞定。简直就是美好一生的东西。而Glide就是这样使人美好一生的东西之一。

    说了这么多,Glide如何加载网络图片?很简单,就上次的三句话:

    ImageView targetImageView = (ImageView) findViewById(R.id.imageView);
    String internetUrl = "http://i.imgur.com/idojSYm.png";
    Glide
        .with(context)
        .load(internetUrl)
        .into(targetImageView);
    

    木有什么乱七八糟的东西,直接传入要加载图片的url就可以了。那么图片加载库有很多,为什么选择Glide呢?很简单,因为它流畅,不卡,尤其是在Listview中。嗯,就是酱~

    加载本地图片

    下表是.load()可以传入的参数及说明

    参数 说明
    .load(String string) string可以为一个文件路径、uri或者url
    .load(Uri uri) uri类型
    .load(File file) 文件
    .load(Integer resourceId) 资源Id,R.drawable.xxx或者R.mipmap.xxx
    .load(byte[] model) byte[]类型
    .load(T model) 自定义类型

    从上面可以看到Glide不仅可以加载网络图片,还可以加载本地图片。可接受的参数有文件路径,uri,文件,资源id等。基本上满足了大部分的需求。虽然加载本地图片不像网络图片那样复杂,但我还是建议使用Glide来加载本地图片。因为它是内存友好的,而且还会“偷偷地”帮我们做很多事情。比如内存缓存,Bitmap复用,修正照片方向等。当然为了满足各种各样的需求,仅仅加载图片是不够的,你还需要对图片进行各种各样的变换,也就是Transformation。后面我们会详细了解的。

    加载自定义数据源

    前面的表格中有一个是我们不熟悉的,就是.load(T model),即自定义的数据源类型。那么如何去实现呢?

    实际上,加载自定义数据源主要是通过ModelLoader接口来实现的。由于没有在实际项目中用到过,这方面的经验比较少。想深入了解的,可以参考这篇文章

    不过从官方Wiki上来看,设计ModelLoader接口的初衷用来加载不同尺寸的图片的。众所周知,Android设备屏幕分辨率千奇百怪,大到2K,小到320p。如果在低分辨率的手机上加载大图,不仅损耗用户流量,而且很容易造成OOM;在高分辨的手机上,加载小图又会出现模糊的情况,用户体验极差。很多时候,为了省事,很多app都会选择一个中间分辨率,然后自适应大小。当然这样做无可厚非,但是有更好的办法,我们为什么不去尝试呢?

    那么如何使用Glide来实现这一具体需求呢?首先你要实现自己的ModelLoader,比较简单的方法是继承BaseGlideUrlLoader

    public interface MyDataModel {
        public String buildUrl(int width, int height);
    } 
    public class MyUrlLoader extends BaseGlideUrlLoader<MyDataModel> {
        @Override
        protected String getUrl(MyDataModel model, int width, int height) {
            // Construct the url for the correct size here.
            return model.buildUrl(width, height);
        }
    }
    

    接下来我们可以这样来加载图片:

    Glide.with(this)
         .using(new MyUrlLoader(this))
         .load(new MyDataModel() {
              @Override
              public String buidUrl(int width, int height) {
                  if (width >= 600) {
                      return url1;
                  } else {
                      return url2;
                    }
              }
          })
         .into(imageView);
    

    .using(new MyUrlLoader(this)):使用我们自己的ModeLoader;
    .load(new MyDataModel()):加载我们自定义的数据源

    这里需要解释下getUrl的三个参数:
    model:你加载的数据源
    width:你加载的图片的宽度(px)
    height:你加载的图片的高度(px)

    这样,我们在高分率的设备上加载大图的url1,在低分辨率的设备上加载小图url2。从而实现了根据不同手机上的像素值大小加载不同尺寸的图片的需求。

    当然如果你不想每次都是用.using(new MyUrlLoader()),就需要实现一个自定义的ModelLoaderFactory并在GlideModule中注册。

    public class MyGlideModule implements GlideModule {
        ...
        @Override
        public void registerComponents(Context context, Glide glide) {
            glide.register(MyDataModel.class, InputStream.class, 
                new MyUrlLoader.Factory());
        }
    }
    

    同时也要在AndroidManifest.xml声明

    <meta-data
        android:name="com.mypackage.MyGlideModule"
        android:value="GlideModule" />
    

    如果你有多个自定义的GlideModule类,那么也要在AndroidManifest.xml中声明多个GlideModule。

    对于上面的加载不同尺寸的图片,Google的2014年I/O大会App中有一篇文章专门用来介绍这个的,地址在这里。大概原理是这样子的:

    在服务端有下面的几个可以加载的url:

    URL 图片大小
    myserver.com/images/__w-200-400-600-800-1000__/session1.jpg 原始尺寸
    myserver.com/images/w200/session1.jpg 200px
    myserver.com/images/w400/session1.jpg 400px
    myserver.com/images/w600/session1.jpg 600px
    myserver.com/images/w800/session1.jpg 800px
    myserver.com/images/w1000/session1.jpg 1000px

    那么客户端如何根据不同的手机分辨率去加载不同的url呢?

    Google是这样做的,下面是核心代码:

    //定义正则表达式
    private static final Pattern PATTERN =
          Pattern.compile("__w-((?:-?\\d+)+)__");
    
    @Override
    protected String getUrl(String model, int width, int height) {
        Matcher m = PATTERN.matcher(model);
        int bestBucket = 0;
        if (m.find()) {
            String[] found = m.group(1).split("-");//拿到可以加载的尺寸数组
            for (String bucketStr : found) {
                bestBucket = Integer.parseInt(bucketStr);
                if (bestBucket >= width) {//刚好大于要加载的尺寸,直接跳出循环
                    // the best bucket is the first immediately
                    // bigger than the requested width
                    break;
                }
            }
            if (bestBucket > 0) {//返回合适尺寸的url
                model = m.replaceFirst("w"+bestBucket);
            }
        }
        return model;
    }
    

    大概的步骤如下:
    1.根据服务端可加载的图片url定义正则表达式
    2.根据正则匹配,获取到可以加载的图片尺寸数组
    3.根据要加载的Imageview的大小,选择合适的尺寸的url
    4.拼接url并返回

    上面的例子中有200,400,600,800,1000是可以加载的,如果你要加载的Imageview的大小为600px,当遍历数组到600时,就会直接跳出循环,返回600px大小图片的url,Glide就会加载600px的图片。

    最后送上一个小demo:https://github.com/Alluretears/GldieDemo

    相关文章

      网友评论

      • 74cf8659ae9a:不错不错,收藏了。

        推荐下,分库分表中间件 Sharding-JDBC 源码解析 17 篇:http://t.cn/R0UfGFT


      • 不可不知不用:自定义url加载图片,刷新出现了图片闪烁的问题
      • 不可不知不用:现在自定义图片URL,遇到下拉刷新,图片会有闪动。感觉像是加载了两次
      • i冰点:请问,MyUrlLoader.Factory(),是怎么来的?

        谢谢!
        c3a981697b5d:自定义的
      • eveo:如果图片有多个路径,先预设几条路径,一条路径如果失败,可以加载另一条路径,以此类推,直到成功加载为止,有这样的功能吗
        eveo:@倾城_之泪 :smiley: 感谢解答!
        倾城_之泪:Glide是不支持加载多图的,对于你的这种情况可以先让Glide加载第一张图,在加载的回调里面判断这次加载是否成功,如果失败的话再进行下一次加载。以此类推。
        int currentLoad = 0;
        String[] imageUrls = new String[] { "url1", "url2", "url3" };

        private void loadImageByIndex(int index) {
        Glide.with(this)
        .load(imageUrls[index])
        .listener(new RequestListener<String, GlideDrawable>() {
        @Override
        public boolean onException(Exception e, String model,
        Target<GlideDrawable> target, boolean isFirstResource) {
        currentLoad++;
        if (currentLoad < imageUrls.length) {
        loadImageByIndex(currentLoad);
        }
        return false;
        }

        @Override
        public boolean onResourceReady(GlideDrawable resource, String model,
        Target<GlideDrawable> target, boolean isFromMemoryCache,
        boolean isFirstResource) {
        return false;
        }
        })
        .into(imageView);
        }

        最后调用loadImageByIndex(0)就好了
      • poker1face:glide加载图片会有个淡入淡出的效果 是默认的吗?怎么取消?
        0b9ed3e9d96c:@倾城_之泪 加载本地drawable大图,一开始会闪屏,有什么方法可以解决吗
        poker1face:@倾城_之泪 我改成了crossFade(0) :smile:
        倾城_之泪:@poker1face 是默认的,使用dontAnimate方法禁用动画
      • lxacoder:为什么我加载本地uri加载不出来,content://media/internal/images/media/166这是我获得的相册图片uri
        倾城_之泪:@lxacoder 先试下直接使用ImageView.setImageURI方法可不可以加载出来,如果也加载不出来,就不是glide的问题。加载出来的话,看下我的这篇文章http://www.jianshu.com/p/9bd6efca8724。调试下集体的原因。
      • 萧喃:@Override
        public void onBindViewHolder(MyGifRecyclerViewAdapter.MyHolder holder, final int position) {
        Log.e(TAG, " ---头像路径-->>>"+ gifsBeens.get(position).getFixed_height_url());
        Glide.with(mContext)
        .load(gifsBeens.get(position).getFixed_height_url())
        // .centerCrop()
        //.fitCenter()
        // .transform(new GlideCircleTransform(mContext))
        .placeholder(R.drawable.load_ing)
        .error(R.mipmap.default_pic1)
        .skipMemoryCache(true)
        .diskCacheStrategy(DiskCacheStrategy.SOURCE)
        .crossFade()
        .into(holder.imageView);
        }
        用recycleView加载gif图片,快速滚动出现OOm怎么解决?
        倾城_之泪:@GoChat 如果你的项目中有大量的gif,我建议你使用Fresco.Glide在gif上的支持并没有Fresco好
      • Kevin_Curry:缓存相关的呢
      • hehe_Android:请问Glide怎么加载高低分辨率的图片
        倾城_之泪:@hehe_Android 可以使用Glide的thumbnail方法:
        Glide.with(this).load(url).thumbnail(Glide.with(this).load(thumbnailUrl)).into(imageView);
        url为大图地址,thumbnailUrl为低分辨率图片地址
        hehe_Android:@倾城_之泪 就是先加载一个低分辨率的图片,后台去 获取一个大图,大图获取完再加载
        倾城_之泪:@hehe_Android 表示没有听懂你的意思
      • jameswan:为什么我老是报错
        You must provide a Model of a type for which there is a registered ModelLoader, if you are using a custom model, you must first call Glide#register with a ModelLoaderFactory for your custom model class
        1c5133cee27b:@jameswan 兄弟解决了吗。我也是这个错误
        空心菜的爱:@jameswan 总感觉你这个是类型没传对,我以前直接传过一个object 也是这个错误的
        倾城_之泪:@jameswan show me the code

      本文标题:使用Glide加载图片系列之一从不同的数据源加载图片

      本文链接:https://www.haomeiwen.com/subject/jrqqhttx.html