欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Glide教程1-4

程序员文章站 2022-06-05 12:41:46
...

1. Glide — 入门

Glide, 就像Picasso一样, 可以从多种来源加载和显示图片,而且也会去兼顾缓存和在做图片处理的时候维持一个低内存消耗的状态。这个库已经在Google官方app中使用(eg: Google I/O 2015),和Picasso一样受欢迎。在这个系列中,我们准备去探索Glide相对于Picasso之间的不同以及优势。

为什么要使用Glide?

有经验的Android开发者可以忽略本章节,但是对于初学者来说:也许可能会问自己,为什么想要去用Glide,而不是自己去实现一个。

Android 在处理图像的时候显得有点耍大牌,因为他会以像素点的形式(pixel by pixel)加载到内存之中。对于手机摄像头来说平均一张普通的照片尺寸为2592x1936像素(5百万像素)会分配19MB的内存。对于复杂的且参差不齐的网络环境,图片缓存和图片处理,如果你使用一个像Glide那样开发和测试完善的库,会省掉你好多时间和不会让你头痛。

在这个系列中,我们看到了很多Glide特性。只要看看这博客的文章提纲概要,然后想想你是否真的要自己去开发所有的这些功能。

添加Glide到你的配置中(Adding Glide to Your Setup)

希望现在我们已经说服你去使用这样的一个库来处理你的图片加载请求。如果你想要了解更多关于Glide,这就是你指南!

首先第一件事,添加Glide到你的依赖,截至发稿时Glide的最新版本为3.7.0

Gradle

与大多数依赖一样,添加下面的一行在你的Gradle项目中的build.gradle

compile 'com.github.bumptech.glide:glide:3.7.0'

Maven

虽然现在我们的项目基本上都是基于Gralde,但是Glide也支持Maven项目:

<dependency>  
    <groupId>com.github.bumptech.glide</groupId>
    <artifactId>glide</artifactId>
    <version>3.7.0</version>
    <type>aar</type>
</dependency>

初体验: 从URL加载图片(First Peek: Loading Image from a URL)

就像Picasso一样,Glide库是使用流接口( fluent interface)。Glide对于一个完成的请求,它的建造者最少需要三个参数:

  • with(Context context) - 对于很多Android API 来说,Context这个参数是必须的。当然Glide也是一样。
  • load(String imageUrl) - 这里指定你哪张图片 需要被加载。很多情况下,它会是一个网络图片的URL的字符串。
  • into(ImageView targetImageView) - targetImageView是你的图片该显示的地方。

纸上谈兵总是难以掌握的,所以我们要看一个实际动手的例子:

ImageView targetImageView = (ImageView) findViewById(R.id.imageView);  
String internetUrl = "http://i.imgur.com/DvpvklR.png";

Glide  
    .with(context)
    .load(internetUrl)
    .into(targetImageView);

是的!如果你的图片URL是存在并且可用的并且你的ImageView是处在显示状态的时候,你将会在几秒后看到你的图片。以防图片不存在,Glide会返回一个错误回调,这个我们往后再看。你可能已经被这三行Glid的代码说服它是对你有用的,但还是它特性的冰山一角。

绑定生命周期
Glide与activity和fragment绑定生命周期很简单,只用在with的时候传入想绑定生命周期的Context就行.

比如通常在MainActivity中传入this,或者MainActivity即可:

Glide.with(this).load(mUrl).into(mIv);

展望(Outlook)

下面,我们将会开始看看除了从网络URL中加载的其他选项。总的来说,我们将会从Android资源,本地文件,一个Uri加载一张图片。


2. Glide — 高级加载

上面,我们已经看了使用Glide的原因以及一个从网络资源加载一张图片的简单例子。但这不仅仅是Glide的唯一加载来源。Glide还可以从Android资源,文件和Uri来加载图片。在这篇博客中,我们会涵盖这三种方式。

从资源中加载(Loading from Resources)

首先是从Android资源加载。你使用资源idint来替换之前的一个指向网络URL的字符串。

int resourceId = R.mipmap.ic_launcher;
Glide  
    .with(context)
    .load(resourceId)
    .into(imageViewResource);

如果你对R.mipmap.感到疑惑,这是Android来处理icon的新方式

当然,你可以直接使用ImageView类里面的方法来设置一个资源。然而,如果你使用更高级的话题例如动态转换,这可能能更有趣。

从文件中加载(Loading from File)

其实是从文件中加载。当你让用户选择一个照片来显示图片(类似于相册)会很有用。参数只是一个File对象。我们来看一个例子:

// this file probably does not exist on your device. However, you can use any file path, which points to an image file

File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Running.jpg");

Glide  
    .with(context)
    .load(file)
    .into(imageViewFile);

从Uri加载(Loading from Uri)

最后,你可以制定一个Uri来加载图片。这个请求和之前的没什么不同:

// this could be any Uri. for demonstration purposes we're just creating an Uri pointing to a launcher icon

Uri uri = resourceIdToUri(context, R.mipmap.future_studio_launcher);

Glide  
    .with(context)
    .load(uri)
    .into(imageViewUri);

一个简单的从资源id转换成Uri的小工具函数。

public static final String ANDROID_RESOURCE = "android.resource://";  
public static final String FOREWARD_SLASH = "/";

private static Uri resourceIdToUri(Context context, int resourceId) {  
    return Uri.parse(ANDROID_RESOURCE + context.getPackageName() + FOREWARD_SLASH + resourceId);
}

然而,这个Uri不一定是从资源id生成的,可以是任何的Uri。

展望(Outlook)

基础加载原则已经完成,现在我们终于可以看到更多有趣的东西。下周我们会涵盖在ListViewGridView的适配器使用和Glide的缓存。



3. Glide — 列表适配器(ListView, GridView)

在这个系列的前两篇文章中展示了如何在ImageView去加载单个图片。这篇博客将会演示每一个项只包含单个ImageViewListViewGridView的适配器实现。这就像是很多相册app那样。

首先,我们需要准备一些测试图片,我们上传了从我们eatfoody.com项目精选的食谱图片。

public static String[] eatFoodyImages = {
        "http://i.imgur.com/rFLNqWI.jpg",
        "http://i.imgur.com/C9pBVt7.jpg",
        "http://i.imgur.com/rT5vXE1.jpg",
        "http://i.imgur.com/aIy5R2k.jpg",
        "http://i.imgur.com/MoJs9pT.jpg",
        "http://i.imgur.com/S963yEM.jpg",
        "http://i.imgur.com/rLR2cyc.jpg",
        "http://i.imgur.com/SEPdUIx.jpg",
        "http://i.imgur.com/aC9OjaM.jpg",
        "http://i.imgur.com/76Jfv9b.jpg",
        "http://i.imgur.com/fUX7EIB.jpg",
        "http://i.imgur.com/syELajx.jpg",
        "http://i.imgur.com/COzBnru.jpg",
        "http://i.imgur.com/Z3QjilA.jpg",
};

第二,我们需要一个创建一个adapter并将它设置给ListView的activity:

public class UsageExampleAdapter extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_usage_example_adapter);

        listView.setAdapter(new ImageListAdapter(UsageExampleAdapter.this, eatFoodyImages));
    }
}

第三,让我们看一下adapter的布局文件。这个ListView的布局文件非常简单:

<?xml version="1.0" encoding="utf-8"?>  
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"  
       android:layout_width="match_parent"
       android:layout_height="200dp"/>

这将会显示一个每一项含有一个高度为200dp和填充设备宽度的图片的图片列表。明显这不是一个最漂亮的相册,但是这并不是这篇博客的重点。

在此之前,我们需要为ListView的实现一个adapter。我们会让它看起来简单和丙丁我们的eatfoody样本图片到adapter。每一个item会显示一个图片。

public class ImageListAdapter extends ArrayAdapter {  
    private Context context;
    private LayoutInflater inflater;

    private String[] imageUrls;

    public ImageListAdapter(Context context, String[] imageUrls) {
        super(context, R.layout.listview_item_image, imageUrls);

        this.context = context;
        this.imageUrls = imageUrls;

        inflater = LayoutInflater.from(context);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (null == convertView) {
            convertView = inflater.inflate(R.layout.listview_item_image, parent, false);
        }

        Glide
            .with(context)
            .load(imageUrls[position])
            .into((ImageView) convertView);

        return convertView;
    }
}

有趣的事情将会发生在ImageListAdaptergetView()方法。你将会看到Glide调用的方法是和之前常规加载图片的方法完全一样。无论你在应用尝试加载什么,Glide调用的方法还是保持不变。

作为一个进阶的Android开发者,你需要知道我们需要重用ListView的布局,来创造一个快速且顺滑滚动的体验。Glide的魅力是它会自动的处理请求的取消,清空ImageView和加载正确的图片到对应的ImageView

Glide教程1-4

Glide的一个优势:缓存(A Strength of Glide: Caching)

当你多次上下滚动,你将会看到图片会比之前显示的更快。在新的设备中,有可能甚至会没有等待时间。就像你猜的那样,这些图片都是从缓存中来的,并不是从网络加载的。Glide的缓存是基于Picasso实现的,所以这对你来说会更加的全面和做这些事情更加轻松。所实现的缓存大小取决于你的磁盘大小。

当你在加载图片的时候,Glide会使用三种来源:内存,磁盘和网络(从最快到最慢)。再次说明,这里并没有什么你必须去完成的。Glide会为你隐藏所有复杂情况的实现,同时为你创建了只能的缓存大小。我们仔细的在以后的博客中看看这缓存的实现。

对于带图片的GridView的实现和ListView的实现并没有什么不同。你其实可以使用相同的adapter。只需要在activity中将布局文件改成GridView的:

<?xml version="1.0" encoding="utf-8"?>  
<GridView  
    android:id="@+id/usage_example_gridview"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:numColumns="2"/>

这就是上面设计的效果图:

Glide教程1-4

其他应用:ImageViews作为元素(Other Applications: ImageViews as Elements)

到目前为止,我们只是看到了整个adapter的item是一个ImageView的例子。如果一个或多个ImageView只是adapter item的一个小部分,Glide的加载方式仍然适用。只是你的getView()方法代码看起来会有一点点不同,但是Glide加载item的方式还是相同的。

展望(Outlook)

在此刻,你已经学习了如何去用Glide加载的90%的Android应用场景。在我们涵盖剩余的案例之前,我们将讲解Glide额外的功能(除了图片加载和缓存)。换句话说,下周我们将会去了解展位图和动画。



4. Glide-默认图与过渡动画(Placeholders & Fade Animations)

在学习完如何从各种源加载图片后,本周将讲述占位符,也就是在图片加载出来前默认显示的东西。

毫无疑问,一个空白的 ImageView 对于任何UI来讲都不太美观,如果你使用Glide,你很可能是通过网络来加载图片。那么根据用户的网络环境,这可能需要花费较长时间。对于App来讲,一个好的做法是在图片加载出来前先显示一个默认的图片。

Glide提供了一个流畅的接口可以轻而易举的实现!只需要调用.placeHolder()并传入一个图片资源ID即可,这样Glide就会在你的图片加载出来前,显示这张默认的图片。

Glide  
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .placeholder(R.mipmap.ic_launcher) // 也可以是一个drawable
    .into(imageViewPlaceholder)

很明显,你不能设置一个网络链接作为这个占位符,因为它也需要网络加载。相比之下,资源文件和drawable则可以保证可用。但是,load()方法的参数可以是任何类型的。但有一些情况可能会加载不出来(比如 没有网络,服务器挂了,等等...),删除或者无法访问。在下一节,我们将来讲解 “异常占位符”。

异常占位符(Error Placeholder): .error()

假设我们的app尝试从网站上加载一张图片,但是这个网站已经挂掉了。Glide提供了一个“加载错误”的回调,并且我们可以采取相应的措施。我们稍后会介绍这中情况,但对于现在而言,那么做太复杂了。大多情况下是使用一个错误占位符,这足以告诉用户图片加载出现异常了。

做法跟我们上面显示默认图片有点类似,只是调用的方法是.error():

Glide  
    .with(context)
    .load("http://futurestud.io/non_existing_image.png")
    .placeholder(R.mipmap.ic_launcher) // 也可以是一个drawable
    .error(R.mipmap.future_studio_launcher) // 一旦图片加载失败,则会显示这个资源id指定的图片
    .into(imageViewError);

就是这样,如果你load()里面指定的图片无法成功加载,那么Glide就会显示R.mipmap.future_studio_launcher来代替。同样的,.error()可以接受的参数只能是已经初始化的drawable或者指向某个资源id(R.drawable.<drawable-keyword>)。

crossFade()的使用(淡入淡出)

如论你是否在图片加载前显示占位符,改变ImageView的图片对于你的界面来讲非常重要。怎样做才能看起来更流畅和简单?一个简单的做法是使用淡入淡出动画.Glide附带了标准的淡入淡入的动画,在(Glide3.6.1版本)后是默认开启的。如果你想强制Glide使用淡入淡出动画,你可以在builder中调用它:

Glide  
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .placeholder(R.mipmap.ic_launcher) 
    .error(R.mipmap.future_studio_launcher) 
    .crossFade()//ps:这个
    .into(imageViewFade);

这个corssFade()方法有另一个形式:.crossFade(int duration),如果你想减慢(或加快)动画速度,随便传一个以ms为单位的动画时长即可,默认的动画时长是300ms。

dontAnimate()的使用(不要动画)

如果你想直接显示图片,不需要动画效果,那么在builder中调用.dontAnimate()即可:

Glide  
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .placeholder(R.mipmap.ic_launcher)
    .error(R.mipmap.future_studio_launcher)
    .dontAnimate()//ps:这个
    .into(imageViewFade);

这样就会直接显示图片到ImageView里面,没有任何过渡动画。请确保你这么做有充分的理由!(ps:因为那样效果不好)

值得一提的是,以上这些参数都是相互独立设置的,不需要相互依赖。例如,你可以不调用.placeholder()而只设置.error()。你也可以在没有设置占位符的情况下设置crossFade()动画,这些参数以任何形式的组合都是可以的。

展望

真心希望你能从这个博客帖子理解并且收获许多。对于提高用户体验来说,不会突然地弹出图片是极其重要的。所以,如果出现加载异常的时候,要用明显的方式告诉用户。Glide提供了一些很容易使用的方法,它能帮你塑造一个更好的应用。

但我们目前还没有做一些优化。下一周,我们将了解图片的调整与缩放。