Glide Custom Iz Able Image

April 7, 2017 | Author: Esteban Dalas Leon | Category: N/A
Share Embed Donate


Short Description

Download Glide Custom Iz Able Image...

Description

Glide: Customizable Image Loading on Android © 2015 - 2016 Norman Peitek

Also By Norman Peitek Picasso: Easy Image Loading on Android Retrofit: Love Working with APIs on Android

Contents About the Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . What Topics are Covered in this Book? . . . . . . . . . . . . . . . . . . . . . . . . . . . . Who is this book for? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

i i i

Chapter 1 — Loading Images, Gifs & Local Videos Why Use Glide? . . . . . . . . . . . . . . . . . Adding Glide to Your Setup . . . . . . . . . . . First Peek: Loading Image from a URL . . . . . Advanced Loading . . . . . . . . . . . . . . . . Displaying Gifs & Videos . . . . . . . . . . . . Chapter Summary . . . . . . . . . . . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

1 1 1 2 3 5 7

Chapter 2 — Image Display & Placeholders . . Adapter Usage (ListView, GridView) . . . . . Sample Gallery Implementation: GridView . Other Applications: ImageViews as Elements Placeholders . . . . . . . . . . . . . . . . . . Fade Animations . . . . . . . . . . . . . . . . Chapter Summary . . . . . . . . . . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

8 8 12 14 24 25 26

Chapter 3 — Image Resizing & Thumbnails . Image Resizing & Scaling . . . . . . . . . Thumbnails . . . . . . . . . . . . . . . . Chapter Summary . . . . . . . . . . . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

28 28 30 32

Chapter 4 — Caching & Request Priorities Caching Basics . . . . . . . . . . . . . . Request Priorities . . . . . . . . . . . . Chapter Summary . . . . . . . . . . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

33 33 36 37

Chapter 5 — Callbacks With Glide Targets . . . . . . . Simple Targets . . . . . . . . . . . . . . . . . . . . . ViewTarget . . . . . . . . . . . . . . . . . . . . . . . Loading Images into Notifications and App Widgets Chapter Summary . . . . . . . . . . . . . . . . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

39 39 42 43 49

. . . .

CONTENTS

Chapter 6 — Exceptions and Debugging Local Debugging . . . . . . . . . . . . General Exception Logging . . . . . . Chapter Summary . . . . . . . . . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

50 50 51 52

Chapter 7 — Glide Transformations . . Transformations . . . . . . . . . . . How to Rotate Your Images . . . . . Collection of Glide Transformations Chapter Summary . . . . . . . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

53 53 60 63 64

Chapter 8 — Glide Animations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Animation Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

66 66 68

Chapter 9 — Glide Modules . . . . . . . . . . . . . . . . . . . . . . . . Integrating Network Stacks . . . . . . . . . . . . . . . . . . . . . . Customize Glide with Modules . . . . . . . . . . . . . . . . . . . . Glide Module Example: Accepting Self-Signed HTTPS Certificates . Customize Memory Cache . . . . . . . . . . . . . . . . . . . . . . Customize Disk Cache . . . . . . . . . . . . . . . . . . . . . . . . . Custom Cache Implementations . . . . . . . . . . . . . . . . . . . Request Images in Certain Dimensions . . . . . . . . . . . . . . . . Dynamic Use of Model Loaders . . . . . . . . . . . . . . . . . . . . Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

69 69 71 74 81 82 84 84 88 90

Chapter 10 — App Release Preparation Enable ProGuard . . . . . . . . . . . Configure ProGuard Rules for Glide Obfuscated Stack Traces . . . . . . . Chapter Summary . . . . . . . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

91 91 92 92 93

Outro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

94

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

About the Book Due to the popularity of the Glide blog post series published on the Future Studio blog, and the positive feedback on our Picasso and Retrofit books, we’ve decided to publish a book on Glide. If your Android app uses images, this book will save you a ton of time researching and avoid stressful evenings of bug fixes. If you value your time, this might be something for you. We cover all topics from the blog post series and additionally add more explanations to each topic and the example code snippets. Besides a more coherent introduction to Glide, you’ll also benefit from more new, book-exclusive advanced topics. This book is for beginners and advanced readers as well. We’ll walk you through each topic with direct reference to code examples. Once you’ve worked through this book, you’ll have an extensive knowledge of image loading on Android with Glide.

What Topics are Covered in this Book? The list below provides a comprehensive overview of covered topics within the book. • • • • • • • • • • •

Introduction to Glide Loading Images, Gifs & Local Videos Image Display & Placeholders Image Resizing & Thumbnails Caching & Request Priorities Callbacks With Glide Targets Exceptions and Debugging Glide Transformations Glide Animations Customizing Glide with Glide Modules App Release Preparations

Who is this book for? This book is for Android developers who want to get an substantial overview and reference book of Glide. You’ll benefit from the clearly recognizable code examples in regard to your daily work with Glide. i

About the Book

ii

Rookie If you’re just starting out with Glide (or coming from any other image loading library like Picasso) this book will show you all important parts on how to work with images. The provided code snippets let you jumpstart and create an image-rich app within minutes.

Expert You already worked with Glide before? You’ll profit from our extensive code snippets and can improve your existing code base. Additionally, the book illustrates various optimizations for an even better user experience.

Chapter 1 — Loading Images, Gifs & Local Videos After a lot of success and feedback on our Picasso¹ series, we’re following the requests to do an extensive series on another amazing image loading library: Glide². The series will be published in the last months of 2015 and beginning of 2016. For the first time, we developed the book alongside with our blog post series. We hope this gives interested users a chance to buy the book before all the blog posts are published. If you bought this book after the last blog post was published, no worries, you’ll still get a ton of additional value with our extra content! Glide, just like Picasso, can load and display images from many sources, while also taking care of caching and keeping a low memory impact when doing image manipulations. It has been used by official Google apps (like the app for Google I/O 2015) and is just as popular as Picasso.

Why Use Glide? Experienced Android developers can skip this section, but for the starters: you might ask yourself why you want to use Glide* instead of your own implementation. Android is quite the diva when working with images, since it’ll load images into the memory pixel by pixel³. A single photo of an average phone camera with the dimensions of 2592x1936 pixels (5 megapixels) will allocate around 19 MB of memory. If you add the complexity of network requests on spotty wireless connections, caching and image manipulations, you will safe yourself a lot of time & headache, if you use a well-tested and developed library like Glide. In this book, we’ll look at many of the features of Glide. Just take a peek at the table of contents and think if you really want to develop all of these features yourself. * = or any other image loading library, like Picasso, ION, etc.

Adding Glide to Your Setup Hopefully by now we’ve convinced you to use a library to handle your image loading requests. If you want to take a look at Glide, this is the guide for you! First things first, add Glide to your dependencies. At the time of writing, the last stable version of Glide is 3.7.0. ¹https://futurestud.io/blog/picasso-series-round-up/ ²https://github.com/bumptech/glide ³http://developer.android.com/training/displaying-bitmaps/index.html

1

Chapter 1 — Loading Images, Gifs & Local Videos

2

Gradle As with most dependencies, pulling it in a Gradle project is a single line in your build.gradle: 1

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

Maven While we moved all our projects to Gradle, Glide also supports Maven projects: 1 2 3 4 5 6

com.github.bumptech.glide glide 3.7.0 aar

First Peek: Loading Image from a URL Just like Picasso, the Glide library is using a fluent interface⁴. The Glide builder requires at least three parameters for a fully functional request: • with(Context context) - Context⁵ is necessary for many Android API calls. Glide is no difference here. Glide makes this very convenient by also extracting the context if you pass an Activity or Fragment object. • load(String imageUrl) - here you specify which image should be loaded. Mostly it’ll be a String representing a URL to an Internet image. • into(ImageView targetImageView) - the target ImageView your image is supposed to get displayed in. Theoretical explanations are always harder to grasp, so let’s look at a hands-on example:

⁴http://en.wikipedia.org/wiki/Fluent_interface ⁵http://developer.android.com/reference/android/content/Context.html

Chapter 1 — Loading Images, Gifs & Local Videos

1 2 3 4 5 6 7

3

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

That’s it! If the image at the URL exists and your ImageView is visible, you’ll see the image in a few seconds. In case the image does not exist, Glide will return to the error callbacks, which we’ll look at later. You might already be convinced with this three-line example that Glide is useful to you, but this is just the tip of the feature iceberg. In the next section, we’ll start by looking at other options to load images, besides from an Internet URL. Specifically, we’ll load an image from Android resources, local files and a Uri.

Advanced Loading In the last section, we’ve looked at reasons for using Glide and a simple example request to load an image from an Internet source. But this is not the only possible image source for Glide. Glide can also load images from the Android resources, files and Uri⁶’s. In this part, we’ll cover all three options.

Loading from Resources First up is loading from Android resources. Instead of giving a String pointing to an Internet URL, you give a resource int. 1 2 3 4 5 6

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

If you’re confused by the R.mipmap., it’s Android’s new way⁷ of handling icons. Of course, you can set a resource directly by using the methods of the ImageView class. However, this can be interesting if you’re using more advanced topics like dynamic transformations. ⁶http://developer.android.com/reference/android/net/Uri.html ⁷http://android-developers.blogspot.de/2014/10/getting-your-apps-ready-for-nexus-6-and.html

Chapter 1 — Loading Images, Gifs & Local Videos

4

Loading from File Second up is loading from a file. This can be useful when you let the user select a photo to display an image (similar to a gallery). The parameter is just a File object. In an example, we look up the file from our external public directory. Note that this snipped most likely won’t work for you (just if you’ve Running.jpg on your phone). 1 2 3 4 5 6 7 8 9 10 11 12

// 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);

Loading from Uri Lastly, you can also load images defined by an Uri. The request is no different from the previous options: 1 2 3 4 5 6 7 8 9

// 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);

The small helper function is a simple conversion from the resourceId to an Uri.

Chapter 1 — Loading Images, Gifs & Local Videos

1 2 3 4 5 6 7

5

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_SLAS\ H + resourceId); }

However, the Uri does not have to be generated from a resourceId. It can be any Uri. The basic loading principles are done, now we can finally look at more interesting stuff. In the next section, we’ll show you how to display Gifs and local videos with Glide.

Displaying Gifs & Videos This section will show you a unique feature of Glide: displaying Gifs and local videos. A lot of image loading libraries ship with the functionality to load and display images. Gif support is something special and very helpful if you need it in your app. Glide makes the experience with Gifs especially amazing because it’s so simple. If you want to display a Gif, you can just use the same regular call you’ve been using in the past with images: 1 2 3 4 5 6 7

String gifUrl = "http://i.kinja-img.com/gawker-media/image/upload/s--B7tUiM5l--/\ gf2r69yorbdesguga10i.gif"; Glide .with( context ) .load( gifUrl ) .into( imageViewGif );

That’s it! This will display the Gif in the ImageView and automatically start playing it. Another great thing about Glide is that you still can use all your standard calls to manipulate the Gif: 1 2 3 4 5 6

Glide .with( context ) .load( gifUrl ) .placeholder( R.drawable.cupcake ) .error( R.drawable.full_cake ) .into( imageViewGif );

Chapter 1 — Loading Images, Gifs & Local Videos

6

Gif Check A potential issue with the code above is, that if the source you provide is not a Gif, and maybe just a regular image, there is no way of registering that issue. Glide accepts Gifs or images as the .load() parameter. If you, the developer, expect that the URL is a Gif, which is not the case, Glide can’t automatically detect that. Thus, they introduced an additional method to force Glide into expecting a Gif .asGif(): 1 2 3 4 5 6

Glide .with( context ) .load( gifUrl ) .asGif() .error( R.drawable.full_cake ) .into( imageViewGif );

If the gifUrl is a gif, nothing changes. However, unlike before, if the gifUrl is not a Gif, Glide will understand the load as failed. This has the advantage, that the .error() callback gets called and the error placeholder gets shown, even if the gifUrl is a perfectly good image (but not a Gif).

Display Gif as Bitmap If your app displays an unknown list of Internet URLs, it might encounter regular images or Gifs. In some cases, you might be interested in not displaying the entire Gif. If you only want to display the first frame of the Gif, you can call .asBitmap() to guarantee the display as a regular image, even if the URL is pointing to a Gif. 1 2 3 4 5

Glide .with( context ) .load( gifUrl ) .asBitmap() .into( imageViewGifAsBitmap );

This should give you all the knowledge to display Gifs with Glide. It’s easy, try it!

Display of Local Videos Another step up from Gifs are videos. Glide is also able to display videos, as long as they’re stored on the phone. Let’s assume you get the file path by letting the user select a video:

Chapter 1 — Loading Images, Gifs & Local Videos

1 2 3 4 5 6

7

String filePath = "/storage/emulated/0/Pictures/example_video.mp4"; Glide .with( context ) .load( Uri.fromFile( new File( filePath ) ) ) .into( imageViewGifAsBitmap );

One more time: it’s important to note that this only works for local videos. Videos, which are not stored on the device (for example Internet URLs), will not work! If you want to display videos from an Internet URL, look into VideoView⁸. After reading this section, you should be able to work with Gifs and local videos as well as you already can with images. Glide makes working with Gifs very smooth and convenient.

Chapter Summary In this chapter, you learned the basics of Glide. You should have learned: • • • • •

[x] What Glide is [x] Why to use Glide [x] How to integrate Glide into your project [x] How to load images from various resources [x] How to load Gifs or local videos

In the next chapter we’ll cover adapter use and Glide’s caching in ListViews and GridViews. We also will show you what placeholders Glide offers. Lastly, will demonstrate Glide’s fade animations.

Additional Chapter Resources • Glide on Github⁹ • Glide’s Javadocs¹⁰ • Android SDK & Android Studio IDE¹¹ ⁸http://developer.android.com/reference/android/widget/VideoView.html ⁹https://github.com/bumptech/glide ¹⁰http://bumptech.github.io/glide/javadocs/latest/com/bumptech/glide/package-summary.html ¹¹https://developer.android.com/sdk/index.html

Chapter 2 — Image Display & Placeholders In the first chapter we’ve shown you the easiest of all cases: loading independent images into a single ImageView without any image manipulations or changes. In this chapter, we’ll show you a bunch of options on how to display images. You’ll learn in much greater depth about the library architecture and what Glide does in the background. After this chapter, you’ll be able to build polished apps with a very good user experience.

Adapter Usage (ListView, GridView) The first chapter we’ve shown you how to load a single image into an ImageView. This section will demonstrate adapter implementations for ListView and GridView, where each cell contains a single ImageView. This is similar to many image gallery apps.

Sample Gallery Implementation: ListView First, we’ll need some test images. We uploaded a selection of the best recipe images from our eatfoody.com¹² project to imgur¹³: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

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", }; ¹²http://eatfoody.com ¹³http://imgur.com/a/uz4uZ

8

Chapter 2 — Image Display & Placeholders

9

Second, we’ll require an activity, which creates an adapter and sets it for a ListView: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

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 ) ); } }

Third, let’s look at the layout files for the adapter. The layout file for a ListView item is very simple: 1 2 3 4



This will result in a list of images, which each will have a height of 200dp and match the device’s width. Obviously, this will not result in the prettiest image gallery, but that’s not the focus of this book. Before we can jump to the result, we’ll need to implement an adapter¹⁴ for the ListView. We’ll keep it simple and bind our eatfoody example images to the adapter. Each item will display one image.

¹⁴http://developer.android.com/reference/android/widget/Adapter.html

Chapter 2 — Image Display & Placeholders

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

10

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; } }

The interesting stuff happens in the getView() method of the ImageListAdapter. You’ll see that the Glide call is exactly the same as in the previously used ‘regular’ loading of images. The way to utilize Glide stays the same, no matter what application you’re trying to cover. As an advanced Android developer you will know that we need to re-use layouts in ListViews to create a fast & smooth scrolling experience. One awesomeness of Glide is that it automatically takes care of the request canceling, clearing of the ImageViews, and loading the correct image into the appropriate ImageView.

Chapter 2 — Image Display & Placeholders

11

Chapter 2 — Image Display & Placeholders

12

A Strength of Glide: Caching When you scroll up and down a lot, you’ll see that the images are displayed much faster than previously. On newer phones, there might be no wait times at all. As you can guess, these images come from cache and are not loaded from the network anymore. Glide’s cache implementation is based on the one from Picasso and thus well rounded and will make things a lot easier for you. The size of the implemented cache depends on the device. In a later chapter, we’ll take a longer look at the caches and their sizes. When loading an image, Glide uses three sources: memory, disk and network (ordered from fastest to slowest). Once again, there is nothing you’ll have to do. Glide hides all that complexity from you, while creating intelligently sized caches for you. Again, we’ll take a closer look at the caching in a later chapter, so don’t worry about it for now.

Sample Gallery Implementation: GridView The implementation for a GridView with image elements is not any different from a ListView implementation. You actually can use the same adapter. Just switch out the activity layout to a GridView: 1 2 3 4 5 6 7



This will result in the following design:

Chapter 2 — Image Display & Placeholders

13

Chapter 2 — Image Display & Placeholders

14

Other Applications: ImageViews as Elements So far, we’ve only looked at examples where the entire adapter item is an ImageView. The approach still applies if one or more ImageViews are only a (small) part of the adapter item. Your getView() code will look a little different, but the loading of the Glide item would be identical. Let’s work through it! First, we’ll need the xml layout. For now, let’s keep it easy: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24



Now, we just have to adjust the adapter we’ve used a bit ago. Our new adapter code:

Chapter 2 — Image Display & Placeholders

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

15

public class AdvancedImageListAdapter extends ArrayAdapter { private Context context; private LayoutInflater inflater; private String[] imageUrls; public AdvancedImageListAdapter(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_advanced, parent, false ); } ImageView iv = (ImageView) convertView.findViewById( R.id.listview_item_advanced_image\ view ); TextView tv = (TextView) convertView.findViewById( R.id.listview_item_advanced_tex\ t ); tv.setText( "Position " + position ); Glide .with( context ) .load( imageUrls[position] ) .asBitmap() .centerCrop() .into( iv );

Chapter 2 — Image Display & Placeholders

43 44 45

16

return convertView; } }

Alright, we’re done! That was easy. You’ll notice an interesting piece in the adapter code: we’re calling .centerCrop() to make sure that the ImageView is completely filled and no white space is left. In case you’d want to make sure that the entire image is displayed, you could switch it to .fitCenter().

Chapter 2 — Image Display & Placeholders

17

Chapter 2 — Image Display & Placeholders

18

Optimized ListView with Holder Pattern The experienced Android developers will see that we’re not really using a holder pattern in our adapter. Thus, we still can improve the performance of our adapter. We don’t need to change anything for the layout. The adapter is the only place which needs improvement: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

public class AdvancedImageListAdapter extends ArrayAdapter { private Context context; private LayoutInflater inflater; private String[] imageUrls; public AdvancedImageListAdapter(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) { ViewHolder viewHolder; if (null == convertView) { convertView = inflater.inflate( R.layout.listview_item_advanced, par\ ent, false ); viewHolder = new ViewHolder(); viewHolder.text = (TextView) convertView.findViewById(R.id.listview_item_advanced_\ text); viewHolder.icon = (ImageView) convertView.findViewById(R.id.listview_item_advanced\ _imageview); convertView.setTag(viewHolder); } else{ viewHolder = (ViewHolder) convertView.getTag(); } viewHolder.text.setText( "Position " + position );

Chapter 2 — Image Display & Placeholders

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53

19

Glide .with( context ) .load( imageUrls[position] ) .asBitmap() .centerCrop() .into( viewHolder.icon ); return convertView; } static class ViewHolder { TextView text; ImageView icon; } }

And our ListView is fast and smooth, even with an image in each row. If you’ve questions to the holder pattern, go and read more in the official developer docs¹⁵.

Rounded ImageView Lately, there seems to be a push towards rounded images. If you take a look at popular apps right now, you’ll see that a lot of images are displayed in a round manner. We could theoretically implement a transformation for Glide, which cuts the image into a circle and then displays it in a regular ImageView. In our experience, that doesn’t work 100% of the time. We’ve moved towards keeping the Glide call the same and instead use a customized ImageView, which takes over the task of cropping: 1 2 3 4 5 6 7 8 9 10 11 12

public class RoundedImageView extends ImageView { public RoundedImageView(Context context) { super( context ); } public RoundedImageView(Context context, AttributeSet attrs) { super( context, attrs ); } public RoundedImageView(Context context, AttributeSet attrs, int defStyle) { super( context, attrs, defStyle ); ¹⁵http://developer.android.com/training/improving-layouts/smooth-scrolling.html

Chapter 2 — Image Display & Placeholders

13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

} public static Bitmap getCroppedBitmap(Bitmap bmp, int radius) { Bitmap sbmp; if (bmp.getWidth() != radius || bmp.getHeight() != radius) { float smallest = Math.min( bmp.getWidth(), bmp.getHeight() ); float factor = smallest / radius; sbmp = Bitmap.createScaledBitmap( bmp, (int) (bmp.getWidth() / factor), (int) (bmp.getHeight() / factor), false ); } else { sbmp = bmp; } Bitmap output = Bitmap.createBitmap( radius, radius, Bitmap.Config.ARGB_8888 ); Canvas canvas = new Canvas( output ); final int color = 0xffa19774; final Paint paint = new Paint(); final Rect rect = new Rect( 0, 0, radius, radius ); paint.setAntiAlias( true ); paint.setFilterBitmap( true ); paint.setDither( true ); canvas.drawARGB( 0, 0, 0, 0 ); paint.setColor( Color.parseColor( "#BAB399" ) ); canvas.drawCircle( radius / 2 + 0.7f, radius / 2 + 0.7f, radius / 2 + 0.1f, paint ); paint.setXfermode( new PorterDuffXfermode( PorterDuff.Mode.SRC_IN ) ); canvas.drawBitmap( sbmp, rect, rect, paint ); return output; } @Override protected void onDraw(Canvas canvas) { Drawable drawable = getDrawable();

20

Chapter 2 — Image Display & Placeholders

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71

21

if (drawable == null) { return; } if (getWidth() == 0 || getHeight() == 0) { return; } Bitmap b = ((BitmapDrawable) drawable).getBitmap(); Bitmap bitmap = b.copy( Bitmap.Config.ARGB_8888, true ); int w = getWidth(), h = getHeight(); Bitmap roundBitmap = getCroppedBitmap( bitmap, w ); canvas.drawBitmap( roundBitmap, 0, 0, null ); } }

We’ve taken the code from stackoverflow¹⁶ and are really satisfied with it. Lastly, we’ve to update the ListView item layout to use the RoundedImageView class instead of the regular ImageView: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18



The adapter can stay the same, since RoundedImageView extends ImageView and we can just keep the generic version of it. Our app now looks pretty neat:

Chapter 2 — Image Display & Placeholders

23

Chapter 2 — Image Display & Placeholders

24

At this point, you’ve learned how to load images with Glide in 90% of the Android use cases. Before we cover the edge cases, we’ll explain additional capabilities of Glide (besides image loading and caching). Namely, the next section will be all about placeholders and fade animations.

Placeholders We probably don’t even have to explain or discuss it: empty ImageViews don’t look good in any UI. If you’re using Glide, you most likely are loading images via an Internet connection. Depending on your user’s environment, this might take a significant amount of time. An expected behavior of an app is to display a placeholder until the image is loaded and processed. Glide’s fluent interface makes this very easy to do! Just call .placeHolder() with a reference to a drawable (resource) and Glide will display that as a placeholder until your actual image is ready. 1 2 3 4 5

Glide .with(context) .load(UsageExampleListViewAdapter.eatFoodyImages[0]) .placeholder(R.mipmap.ic_launcher) // can also be a drawable .into(imageViewPlaceholder);

For obvious reasons, you cannot set an Internet url as placeholder, since that one would be required to be loaded as well. App resources and drawables are guaranteed to be available and accessible. However, as the parameter of the .load() method, Glide accepts all kind of values. These might not be loadable (no Internet connection, server down, …), deleted or not accessible. In the next section, we’ll talk about an error placeholder.

Error Placeholder: .error() Let’s assume our app tries to load an image from a website, which is currently down. Glide does give us the option to get an error callback and take the appropriate action. We’ll cover that option later, for now this would be too complicated. In most use cases a placeholder, which signals that the image could not be loaded is sufficient enough. The call to Glide’s fluent interface is identical to the previous example for our pre-display placeholder, just with a different function call named .error():

Chapter 2 — Image Display & Placeholders

1 2 3 4 5 6 7

25

Glide .with(context) .load("http://futurestud.io/non_existing_image.png") .placeholder(R.mipmap.ic_launcher) // can also be a drawable .error(R.mipmap.future_studio_launcher) // will be displayed if the image cannot be loaded .into(imageViewError);

That’s it. If the image you define as .load() value cannot be loaded, Glide will display R.mipmap.future_studio_launcher instead. Once again, acceptable parameters for .error() are only already initialized drawable objects or pointers to their resources (like R.drawable.).

.fallback() Placeholder The .error() placeholder is triggered when the resource is not available, for example the image link is down. Another type of “error” is if you pass a null value to Glide. For example, this could happen if you’ve a ListView with profile images. Not every profile has an image set and thus you’re passing a null value. If you want to specify a good replacement image when you pass null, use .fallback(): 1 2 3 4 5 6 7

String nullString = null; // could be set to null dynamically Glide .with(context) .load( nullString ) .fallback( R.drawable.floorplan ) .into( imageViewNoFade );

The rules are identical to .placeholder(): you can only pass a drawable or a resource id.

Fade Animations No matter if you’re displaying a placeholder before loading the image or not, changing an image of an ImageView is a pretty significant change in your UI. A simple option to make this change more smoothly and easier on the eye, is to use a crossfade animation.

Use of .crossFade() Glide ships with a standard crossfade animation, which is (for the current version 3.7.0) active by default. If you want to force Glide to show the crossfade animation, all you’ve to do is a call to .crossFade() on the builder:

Chapter 2 — Image Display & Placeholders

1 2 3 4 5 6 7 8

26

Glide .with(context) .load(UsageExampleListViewAdapter.eatFoodyImages[0]) .placeholder(R.mipmap.ic_launcher) // can also be a drawable .error(R.mipmap.future_studio_launcher) // will be displayed if the image cannot be loaded .crossFade() .into(imageViewFade);

The crossFade() method has another method signature: .crossFade(int duration). If you want to slow down (or speed up) the animation, feel free to pass a time in milliseconds to the methods. The default duration of the animation is 300 milliseconds.

Use of .dontAnimate() If you wish to directly display the image without the small crossfade effect, call .dontAnimate() on the Glide request builder: 1 2 3 4 5 6 7 8

Glide .with(context) .load(UsageExampleListViewAdapter.eatFoodyImages[0]) .placeholder(R.mipmap.ic_launcher) // can also be a drawable .error(R.mipmap.future_studio_launcher) // will be displayed if the image cannot be loaded .dontAnimate() .into(imageViewFade);

This would directly show you the image, without fading it into the ImageView. Please make sure you’ve a good reason for doing this though! It is important to know that all these parameters are independent and can be set without relying on each other. For example, you could just set .error() without calling .placeholder(). You could set the crossfade animations without the placeholders. Any combination of the parameters is possible. Hopefully you understood and learned a lot from this section. It is tremendously important for a good user experience that the images are not popping in unexpectedly. Also, make it obvious to the user when something goes wrong. Glide assists you with easy to call functions, which provide the things you need to have a better app.

Chapter Summary In this chapter, you’ve learned a whole set of new usages and capabilities of Glide:

Chapter 2 — Image Display & Placeholders

• • • •

27

[x] How to use Glide in adapter for ListView or GridView [x] That Glide will automatically cancel and re-load images when the user scrolls [x] What options Glide has as placeholders [x] How to utilize and change Glide’s fade animation

But we’re not done with learning details about Glide yet. In the next chapter, we’ll look at image resizing and scaling.

Chapter 3 — Image Resizing & Thumbnails In the last chapters, you’ve learned how to load images from various sources and how to use different kinds of placeholders. The goal for this chapter is to learn what to do when you need to change the size or scale of images. Also, by the end of the chapter you’ll know some tools you can use to deal with very large images.

Image Resizing & Scaling In one of the last chapters, we’ll look at Glide modules, which are a way to customize Glide’s behavior. We’ll also show you a Glide module, which requests images from the server in a specific size. For example, if your ImageView is 300x300 pixel, the ideal image would have the identical size. Unfortunately, you can’t always control the size of the source image. In this section, we’ll look at image resizing, which can help to improve your app’s performance when you’ve to deal with large images.

Image Resizing with .resize(x, y) Generally it’s optimal if your server or API deliver the image in the exact dimensions you need, which are a perfect trade-off between bandwidth, memory consumption and image quality. In comparison to Picasso, Glide is much more efficient¹⁷ memory-wise. Glide automatically limits the size of the image it holds in cache and memory to the ImageView dimensions. Picasso has the same ability, but requires a call to .fit(). With Glide, if the image should not be automatically fitted to the ImageView, call override(horizontalSize, verticalSize). This will resize the image before displaying it in the ImageView. 1 2 3 4 5 6

Glide .with(context) .load(UsageExampleListViewAdapter.eatFoodyImages[0]) .override(600, 200) // resizes the image to these dimensions (in pixel). // does not respect aspect ratio .into(imageViewResize); ¹⁷http://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en

28

Chapter 3 — Image Resizing & Thumbnails

29

This option might also be helpful when you load images even though there is no target view with known dimension yet. For example, if the app wants to warm up the cache in the splash screen, it can’t measure the ImageViews yet. However, if you know how large the images should be, use .override() to provide a specific size.

Scaling Images Now, as with any image manipulation, resizing images can really distort the aspect ratio and uglify the image display. In most of your use cases, you want to prevent this from happening. Glide offers the general transformations to manipulate the image display. It ships with two standard options centerCrop and fitCenter. CenterCrop CenterCrop() is a cropping technique that scales the image so that it fills the requested bounds of the ImageView and then crops the extra. The ImageView will be filled completely, but the entire image

might not be displayed. 1 2 3 4 5 6 7 8

Glide .with(context) .load(UsageExampleListViewAdapter.eatFoodyImages[0]) .override(600, 200) // resizes the image to these dimensions (in pixel) .centerCrop() // this cropping technique scales the image // so that it fills the requested bounds // and then crops the extra. .into(imageViewResizeCenterCrop);

FitCenter fitCenter() is a cropping technique that scales the image so that both dimensions are equal to or less than the requested bounds of the ImageView. The image will be displayed completely, but might not fill the entire ImageView. 1 2 3 4 5 6

Glide .with(context) .load(UsageExampleListViewAdapter.eatFoodyImages[0]) .override(600, 200) .fitCenter() .into(imageViewResizeFitCenter);

We’ll look at custom transformations, besides centerCrop() and fitCenter() in a later chapter. For now, you’ve learned how to make adjustments to the size and display of images. This should be very helpful for creating great apps.

Chapter 3 — Image Resizing & Thumbnails

30

Thumbnails So far, we’ve looked at ways to optimize the user experience by reducing the size of the image. Unfortunately, that might not always is what you want. If you’re implementing a gallery app, you require that the image is being displayed with the full resolution, but you still would like to avoid the long waiting time. If the image is very large, the processing can take a significant part of the waiting time until the image is displayed. In this section, we’ll explore thumbnails as another optimization option to preserve the original size of the image.

Advantages of Thumbnails Thumbnails are different than the placeholders from our last chapter. Placeholders have to be shipped with the app as a bundled resource. Thumbnails are a dynamic placeholder, that can be loaded from the Internet as well. Thumbnails will be displayed until the actual request is loaded and processed. If the thumbnail, for whatever reason, arrives after the original image, it does not replace the original image. It simply will be dismissed. Hint: another really nice way to smoothen the image loading process is to tint the placeholder background of the image in the dominant color for the image. We’ve written a blog post¹⁸ for that as well.

Simple Thumbnails Glide offers two different ways for thumbnails. The first is the simple option where the original image is used, just in a smaller resolution. This method is particularly useful in combinations of ListView and detail views. If you already display the image in the ListView, let’s just say, in 250x250 pixels, the image will need a much bigger resolution in the detail view. However, from the user perspective, he already saw a small version of the image, why is there a general placeholder for a few seconds until the same image gets displayed again (in a higher resolution)? In this case, it makes a lot more sense to continue to display the 250x250 pixel version in the detail view and in the background load the full resolution. Glide makes this possible with the .thumbnail() method. In this case, the parameter is a float as the size multiplier: 1 2 3 4 5

Glide .with( context ) .load( UsageExampleGifAndVideos.gifUrl ) .thumbnail( 0.1f ) .into( imageView2 ); ¹⁸https://futurestud.io/blog/how-to-get-dominant-color-code-for-picture-with-nodejs/

Chapter 3 — Image Resizing & Thumbnails

31

For example, if you pass 0.1f as the parameter, Glide will display the original image reduced to 10% of the size. If the original image has 1000x1000 pixels, the thumbnail will have 100x100 pixels. Since the image will be much smaller than the ImageView, you need to make sure the ScaleType¹⁹ of it is set correctly. Note that all request settings you applied to the original request are also applied to the thumbnail. For example, if you used a transformation to make the image gray-scale, the same will happen to the thumbnail.

Advanced Thumbnails with Complete Different Requests While the usage of .thumbnail() with a float parameter is easy to set up and can be very effective, it doesn’t always make sense. If the thumbnail needs to load the same full-resolution image over the network, it might not be faster at all. Thus, Glide provides another option to load and display a thumbnail. The second option is to pass a complete new Glide request as the parameter. Let’s look at an example: 1 2 3 4 5 6 7 8 9 10 11 12 13

private void loadImageThumbnailRequest() { // setup Glide request without the into() method DrawableRequestBuilder thumbnailRequest = Glide .with( context ) .load( eatFoodyImages[2] ); // pass the request as a a parameter to the thumbnail request Glide .with( context ) .load( UsageExampleGifAndVideos.gifUrl ) .thumbnail( thumbnailRequest ) .into( imageView3 ); }

The difference is that the first thumbnail request is completely independent of the second original request. The thumbnail can be a different resource or image URL, you can apply different transformations on it, and so on. Hint, if you want to get crazy, you could make this recursive and apply an additional thumbnail request to the thumbnail request … This section showed you two different approaches to load thumbnails for images with Glide. Don’t forget this option for your polished apps! It can significantly help to decrease the empty ImageView times in your app. ¹⁹http://developer.android.com/reference/android/widget/ImageView.ScaleType.html

Chapter 3 — Image Resizing & Thumbnails

32

Chapter Summary In this chapter, you’ve learned essential tools for every Android developer, who wants to use Glide. It’s often very important to adjust the size of images, which was one of the important take-aways of this chapter. You should have learned: • • • •

[x] Why and when you need to change the size of images [x] How to change the size of images [x] How to avoid distorted image aspect ratios when resizing images [x] What Glide thumbnails are and how they’re different than placeholders

In the next chapter, we’ll finally go deeper into a topic we’ve mentioned a few times before: caching!

Chapter 4 — Caching & Request Priorities In the first three chapters, we’ve introduced you to a lot of the Glide essentials. No worries, there is much more to come! In this chapter, we’ll go under the hood and understand the way various Glide caches work. Additionally, we’ll show you how to prioritize image requests.

Caching Basics After we’ve looked at loading, displaying and manipulating images, we’ll move on to optimizing the process. One of the fundamental features of successful and efficient image loading is caching. In this section, we’ll go through the basics of caching in Glide. A well implemented image loading component attempts to minimize the number of network requests the Android app has to make. Glide is no different here. Glide uses memory and disk caching by default to avoid unnecessary network requests. We’ll look at the exact implementation details in a later chapter. If you can’t wait until then, feel free to browse the official document²⁰ on the topic or skip ahead for a quick read. For now, the important take away is that all image requests get cached in memory and on disk. While the caching is generally something very useful, in some scenarios it might not be the wished behavior. In the next section, we’ll look at how to change Glide’s caching behavior for a single request.

Using Cache Strategies If you played around with Glide in the past, you noticed that you don’t need to do anything extra to activate caching. It comes right out of the box. However, if you know that an image changes rapidly, you might want to avoid certain caches. Glide offers methods to adapt the memory and disk cache behavior. Let’s look at the memory cache first. Memory Cache Let’s visualize it with a very simple request: loading an image from the Internet to an ImageView: ²⁰https://github.com/bumptech/glide/wiki/Caching-and-Cache-Invalidation

33

Chapter 4 — Caching & Request Priorities

1 2 3 4 5

34

Glide .with( context ) .load( eatFoodyImages[0] ) .skipMemoryCache( true ) .into( imageViewInternet );

You already noticed that we called .skipMemoryCache( true ) to specifically tell Glide to skip the memory cache. This means that Glide will not put the image in the memory cache. It’s important to understand, that this only affects the memory cache! Glide will still utilize the disk cache to avoid another network request for the next request to the same image URL. It’s also good to know that Glide will put all image resources into the memory cache by default. Thus, a specific call .skipMemoryCache( false ) is not necessary. Hint: beware of the fact, that if you make an initial request to the same URL without the .skipMemoryCache( true ) and then with the method, the resource will get cached in memory. Make sure you’re consistent across all calls to the same resource, when you want to adjust the caching behavior! Skipping Disk Cache As you’ve learned in the section above, even if you deactivate memory caching, the request image will still be stored on the device’s disk storage. If you’ve an image, which sits behind the same URL, but is changing rapidly, you might want to deactivate disk caching as well. You can change Glide’s behavior of disk caching with the .diskCacheStrategy() method. Unlike the .skipMemoryCache() method, it takes an enum with different values instead of a simple boolean. If you want deactivate the disk cache for a request, use the enum value DiskCacheStrategy.NONE as the parameter: 1 2 3 4 5

Glide .with( context ) .load( eatFoodyImages[0] ) .diskCacheStrategy( DiskCacheStrategy.NONE ) .into( imageViewInternet );

The image in this code snippet will not be saved in the disk cache at all. However, by default it’ll still use the memory cache! In order to deactivate both caches, combine the method calls:

Chapter 4 — Caching & Request Priorities

1 2 3 4 5 6

35

Glide .with( context ) .load( eatFoodyImages[0] ) .diskCacheStrategy( DiskCacheStrategy.NONE ) .skipMemoryCache( true ) .into( imageViewInternet );

Customize Disk Cache Behavior As we’ve mentioned previously, Glide has more than one option for the disk cache behavior. Before we show you the options, you’ve to understand that Glide’s disk caching is fairly complex. For example, Picasso²¹ only caches the full-blown image. Glide, however, caches the original, fullresolution image and additionally smaller versions of that image. For example, if you request an image with 1000x1000 pixels, and your ImageView is 500x500 pixels, Glide will put both versions of the image in the cache. Now you’ll understand the difference between the enum parameters for the .diskCacheStrategy() method: • DiskCacheStrategy.NONE caches nothing, as discussed • DiskCacheStrategy.SOURCE caches only the original full-resolution image. In our example above that would be the 1000x1000 pixel one • DiskCacheStrategy.RESULT caches only the final image, after reducing the resolution (and possibly transformations) • DiskCacheStrategy.ALL caches all versions of the image (default behavior) As a last example, if you’ve an image which you know you’ll manipulate often and make a bunch of different versions of it, it makes sense to only cache the original resolution. Thus, we’d use DiskCacheStrategy.SOURCE to tell Glide to only keep the original: 1 2 3 4 5

Glide .with( context ) .load( eatFoodyImages[2] ) .diskCacheStrategy( DiskCacheStrategy.SOURCE ) .into( imageViewFile );

In this section, you’ve learned how the basics of Glide’s image caching work and how you can adjust the behavior to your needs. In a later chapter, we’ll circle back to this topic to do more advanced optimizations. Nevertheless, for a start this will give you very effective methods to get the most out of the caching behavior of Glide. In the next section, we’ll take a look at another critical piece of a good user experience: prioritizing image requests! ²¹https://futurestud.io/blog/tag/picasso/

Chapter 4 — Caching & Request Priorities

36

Request Priorities Fairly often, you’ll run into use cases, where your app will need to load multiple images at the same time. Let’s assume you’re building an info screen which has a large hero image at the top and two smaller, less critical images at the bottom. For the user experience it’d be optimal that the hero image element is getting loaded and displayed first, then afterwards the less urgent ImageViews at the bottom. Glide supports your wished behavior with the Priority enum and the .priority() method. But before we look at the example and the method calls, let’s look at the priority enum, which is used as the parameter for the .priority() method, first.

Getting to Know the Priority Enum The enum gives you four different options. This is the list ordered with increasing priority: • • • •

Priority.LOW Priority.NORMAL Priority.HIGH Priority.IMMEDIATE

Before we jump to the example, you should know that the priorities are not completely strict. Glide will use them as a guideline and process the requests with the best effort possible, but it’s not guaranteed that all images will be loaded in the requested order. Nevertheless, if you’ve a use case where certain images are more important, make good use of it!

Usage Example: Hero Element with Child Images Let’s go back to our example from the beginning. You’re implementing an information detail page with a hero image at the top and smaller images at the bottom. For the best user experience, the hero image should be loaded first. Thus, we’d assign Priority.HIGH to it. Theoretically, that should be enough. But to make the example a little more interesting, let’s also assign the bottom images the low priority with the .priority(Priority.LOW) call:

Chapter 4 — Caching & Request Priorities

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

37

private void loadImageWithHighPriority() { Glide .with( context ) .load( UsageExampleListViewAdapter.eatFoodyImages[0] ) .priority( Priority.HIGH ) .into( imageViewHero ); } private void loadImagesWithLowPriority() { Glide .with( context ) .load( UsageExampleListViewAdapter.eatFoodyImages[1] ) .priority( Priority.LOW ) .into( imageViewLowPrioLeft ); Glide .with( context ) .load( UsageExampleListViewAdapter.eatFoodyImages[2] ) .priority( Priority.LOW ) .into( imageViewLowPrioRight ); }

If you run this example, you’ll see that in almost all cases the hero images will be displayed first, despite being much larger image (and consequently requiring more processing time). Glide gives you very convenient options to prioritize images. It’s a fast and easy way to bump up the user experience a bit. Go through your apps and your code and see if you can apply the techniques you’ve just learned! Request priorities are very helpful, but don’t always completely solve the problem. Let’s assume you’ve to download a very large image, it’ll take a while to download and process it, no matter what you set as a priority. In that case you can go back to the previous chapter and study if thumbnails can help you out.

Chapter Summary In this chapter, you’ve learned the basics of Glide’s caching component and how to utilize request priorities. You should be able to: • [x] Explain Glide’s caches and in which order they’re accessed • [x] Know the default settings for the caches • [x] How to request images to be/not to be stored in the memory cache

Chapter 4 — Caching & Request Priorities

38

• [x] How to request images to be/not to be stored in the disk cache • [x] Change the request priority of requests In the chapter, we’ll look at a very interesting use case: what if you don’t want to load the image into an ImageView? Instead, maybe you want to load it into notifications, app widgets or custom views? We’ll show you everything, just keep reading.

Chapter 5 — Callbacks With Glide Targets You already have learned a lot about Glide. You’re getting familiar with Glide’s design and how they approach certain functions. We hope you’re having a certain level of comfort with the library, because we’re moving on to a more complex, but very interesting topic: targets. Whenever you want to request and load an image, which is not going to be displayed in a regular ImageView, this will be your chapter. We’ll learn everything to know to load images in non-standard situations!

Simple Targets This section of the chapter will be about using callback techniques with Glide. So far, we’ve always assumed we’re loading the images or Gifs into an ImageView. But that might not always be the case. Next, we’ll look at ways to get the Bitmap of an image resource without specifying an ImageView.

Callbacks in Glide: Targets So far we’ve used the convenient Glide builder to load images into an ImageView. Glide hides a ton of complexity of what happens behind the scenes. Glide does all the network requests and processing in a background thread and, once the result is ready, changes back to the UI thread and updates the ImageView. In this section, we assume that we won’t have an ImageView as the destination for the image. We rather want the Bitmap itself. Glide offers an easy way to access the Bitmap of an image resource with Targets. Targets are nothing else than callbacks, which return the result after Glide’s asynchronous thread is done with all the loading and processing. Glide offers various kinds of targets while each has an explicit purpose. We’ll walk through them in the next few sections. We start with the SimpleTarget.

SimpleTarget So let’s look at a code example:

39

Chapter 5 — Callbacks With Glide Targets

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

40

private SimpleTarget target = new SimpleTarget() { @Override public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) { // do something with the bitmap // for demonstration purposes, let's just set it to an ImageView imageView1.setImageBitmap( bitmap ); } }; private void loadImageSimpleTarget() { Glide .with( context ) // could be an issue! .load( eatFoodyImages[0] ) .asBitmap() .into( target ); }

The first part of the snippet creates a field object, that declares a function, which is called once Glide has loaded and processed the image. The callback function passes the Bitmap as a parameter. You can then use the Bitmap object for whatever use you need it. The second part of the snippet is how we use targets with Glide: exactly the same as with ImageViews! You can pass either, a Target or an ImageView, as parameter to the .into() method. Glide will do its magic and return the result to either one. There is one difference, we added the line .asBitmap(), which forces Glide to return a Bitmap object. Remember, that Glide can also load Gifs or videos. In order to prevent a clash between the target (which expects a Bitmap) and the unknown resource on the Internet behind the URL (which could be a Gif), we can call .asBitmap() to tell Glide to only understand the request as successful, if the resource is an image.

Pay Attention with Targets Besides knowing how to implement a simple version of Glide’s Target callback system, you’ve learned two additional things. The first is the field declaration of the SimpleTarget object. Technically, Java/Android would allow you to declare the target anonymously in the .into() method. However, this significantly increases the chance that the Android garbage collector removes the anonymous target object before Glide was done with the image request. Eventually, this would lead to a situation, where the image is loaded, but the callback can never be called. So please make sure you declare your callback objects as field objects, so you’re protecting it from the evil Android garbage collector ;-) The second critical part is the Glide builder line .with( context ). The issue here is actually a feature of Glide: when you pass a context, for example the current app activity, Glide will automatically stop the request when the requesting activity is stopped. This integration into

Chapter 5 — Callbacks With Glide Targets

41

the app’s lifecycle is usually very helpful, but can be difficult to work with, if your target is independent of the app’s activity lifecycle. The solution here is to use the application context: .with( context.getApplicationContext() ). Then Glide will only kill the image request, when the app is completely stopped itself. Please keep that in mind. Once again, if your request needs to be outside of the activity lifecycle, use the following snippet: 1 2 3 4 5 6 7

private void loadImageSimpleTargetApplicationContext() { Glide .with( context.getApplicationContext() ) // safer! .load( eatFoodyImages[1] .asBitmap() .into( target2 ); }

Target with Specific Size Another potential issue with targets is that they don’t have a specific size. If you pass an ImageView as the parameter for .into(), Glide will use the size of the ImageView to limit the size of the image. For example, if the loaded image is 1000x1000 pixels, but the ImageView only 250x250 pixels, Glide will reduce the image to the smaller size to save processing time and memory. Obviously, this doesn’t work with targets, since there is no known size. However, if you have a specific size in mind, you can enhance the callback. If you know how large the image should be, you should specify it in your callback declaration to save some memory: 1 2 3 4 5 6 7 8 9 10 11 12 13 14

private SimpleTarget target2 = new SimpleTarget( 250, 250 ) { @Override public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) { imageView2.setImageBitmap( bitmap ); } }; private void loadImageSimpleTargetApplicationContext() { Glide .with( context.getApplicationContext() ) // safer! .load( eatFoodyImages[1] ) .asBitmap() .into( target2 ); }

The only difference to the “normal” target declaration is the specification of the size in pixels: new SimpleTarget( 250, 250 ). This should give you all the knowledge to implement SimpleTargets in your app.

Chapter 5 — Callbacks With Glide Targets

42

ViewTarget The reason we can’t use an ImageView directly can be various. We’ve shown you above how to access a Bitmap. Now, we’re going one step further. Let’s assume you’ve a Custom View²². Glide doesn’t support the loading of images into custom views, since there is no way of knowing where the image should be set. However, Glide makes it much easier with ViewTargets. Let’s look at our simple custom view, which extends FrameLayout and internally uses an ImageView and overlays it with a TextView: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

public class FutureStudioView extends FrameLayout { ImageView iv; TextView tv; public void initialize(Context context) { inflate( context, R.layout.custom_view_futurestudio, this ); iv = (ImageView) findViewById( R.id.custom_view_image ); tv = (TextView) findViewById( R.id.custom_view_text ); } public FutureStudioView(Context context, AttributeSet attrs) { super( context, attrs ); initialize( context ); } public FutureStudioView( Context context, AttributeSet attrs, int defStyleAttr) { super( context, attrs, defStyleAttr ); initialize( context ); } public void setImage(Drawable drawable) { iv = (ImageView) findViewById( R.id.custom_view_image ); iv.setImageDrawable( drawable ); } } ²²https://developer.android.com/training/custom-views/index.html

Chapter 5 — Callbacks With Glide Targets

43

You cannot use the regular .into() method of Glide here, since our custom view doesn’t extend ImageView. Thus, we’ve to create a ViewTarget and use that for the .into() method: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

private ViewTarget viewTarget; private void loadImageViewTarget() { FutureStudioView customView = (FutureStudioView) findViewById( R.id.custom_view ); viewTarget = new ViewTarget( customView ) { @Override public void onResourceReady( GlideDrawable resource, GlideAnimation

Chapter 5 — Callbacks With Glide Targets

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50



The following code creates a custom notification with the layout above for us. 1 2 3 4 5 6 7 8 9 10

final RemoteViews rv = new RemoteViews( context.getPackageName(), R.layout.remoteview_notification ); rv.setImageViewResource( R.id.remoteview_notification_icon, R.mipmap.future_studio_launcher );

46

Chapter 5 — Callbacks With Glide Targets

11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

47

rv.setTextViewText(R.id.remoteview_notification_headline, "Headline"); rv.setTextViewText(R.id.remoteview_notification_short_message, "Short Message"); // build notification NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context) .setSmallIcon(R.mipmap.future_studio_launcher) .setContentTitle("Content Title") .setContentText("Content Text") .setContent(rv) .setPriority( NotificationCompat.PRIORITY_MIN); final Notification notification = mBuilder.build(); // set big content view for newer androids if (android.os.Build.VERSION.SDK_INT >= 16) { notification.bigContentView = rv; } NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); mNotificationManager.notify(NOTIFICATION_ID, notification);

This snipped creates three important objects for us, the notification and the RemoteViews and our constant NOTIFICATION_ID. We’ll need those to create the notification target: 1 2 3 4 5 6 7 8 9 10

private NotificationTarget notificationTarget; ... notificationTarget = new NotificationTarget( context, rv, R.id.remoteview_notification_icon, notification, NOTIFICATION_ID);

Lastly, we need to call Glide itself and as we’ve done in the previous posts, set the target as the .into() parameter:

Chapter 5 — Callbacks With Glide Targets

1 2 3 4 5

48

Glide .with( context.getApplicationContext() ) // safer! .load( eatFoodyImages[3] ) .asBitmap() .into( notificationTarget );

The result is that as soon as Glide has the image loaded, it’ll get displayed in our custom notification. Awesome :)

App Widgets Let’s move on to another Glide target. App widgets are a part of Android since quite a while. If your app provides app widgets and contains images, this should be interesting to you. The AppWidgetTarget²⁴ of Glide can help significantly to make this very straight-forward. Let’s look at a simple sample AppWidgetProvider: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

public class FSAppWidgetProvider extends AppWidgetProvider { private AppWidgetTarget appWidgetTarget; @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { RemoteViews rv = new RemoteViews( context.getPackageName(), R.layout.custom_view_futurestudio ); appWidgetTarget = new AppWidgetTarget( context, rv, R.id.custom_view_image, appWidgetIds ); Glide .with( context.getApplicationContext() ) // safer! .load( GlideExampleActivity.eatFoodyImages[3] ) .asBitmap() ²⁴http://bumptech.github.io/glide/javadocs/latest/com/bumptech/glide/request/target/AppWidgetTarget.html

Chapter 5 — Callbacks With Glide Targets

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

49

.into( appWidgetTarget ); pushWidgetUpdate(context, rv); } public static void pushWidgetUpdate(Context context, RemoteViews rv) { ComponentName myWidget = new ComponentName( context, FSAppWidgetProvider.class ); AppWidgetManager manager = AppWidgetManager.getInstance(context); manager.updateAppWidget(myWidget, rv); } }

The important lines are the declaration of the appWidgetTarget object and the Glide builder. Good news here: you don’t need to customize AppWidgetTarget any further with overriding the onResourceReady method. Glide does everything automatically for you. Very nice!

Chapter Summary It’s time to conclude our adventure into Glide targets. You’ve learned how to load images asynchronously for all kind of purposes, ImageViews, notifications, Bitmap callbacks etc. You should be able to: • • • • •

[x] Explain what Glide targets are [x] How to use SimpleTarget to access the bitmap of an image [x] Use ViewTarget to load images into custom views [x] Use NotificationTarget to load images into notifications [x] Use AppWidgetTarget to load images into app widgets

Next, we’ll look how to deal with errors. What happens when something went wrong? What happens when the URL doesn’t exist or isn’t valid? Stay tuned.

Chapter 6 — Exceptions and Debugging After introducing all fundamental concepts of Glide, we’ll move to a developer topic. In this chapter, we’ll show you a few helpful ways to debug issues you might experience during the image loading process with Glide.

Local Debugging Glide’s GeneralRequest²⁵ class offers a method to set the log level. Unfortunately, you don’t have easy access to the class in production use. Nevertheless, there is a very simple way to get Glide’s debug logs. All you’ve to do is to activate it via the adb shell. Open your terminal and use the following command: 1

adb shell setprop log.tag.GenericRequest DEBUG

The last part DEBUG comes from the standard Android log constants²⁶. Thus, your options with increasing priority as the parameter are: • • • • •

VERBOSE DEBUG INFO WARN ERROR

The output, in case of a non-existing image, would look like this: 1 2 3 4

io.futurestud.tutorials.glide D/GenericRequest: load failed io.futurestud.tutorials.glide D/GenericRequest: java.io.IOException: Request fai\ led 404: Not Found ...

As you already guessed, this only works when you have physical access to the device and while you’re developing and testing your app. For logging in production app, you’ll need a different way. The answer is once again callbacks, which we’ll explore in the next section. ²⁵http://bumptech.github.io/glide/javadocs/340/com/bumptech/glide/request/GenericRequest.html ²⁶http://developer.android.com/reference/android/util/Log.html

50

Chapter 6 — Exceptions and Debugging

51

General Exception Logging Glide doesn’t offer direct access to the GenericRequest class to set the logging, but you can catch the exception in case something goes wrong with the request. For example, if an image is not available, Glide would (silently) throw an exception and show the drawable you’ve specified in .error(). If you explicitly want to know the exception, create a listener and pass it to the .listener() method on the Glide builder. First, create the listener as a field object to avoid that it gets garbage collected: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

private RequestListener requestListener = new RequestListener() { @Override public boolean onException( Exception e, String model, Target target, boolean isFirstResource) { // todo log exception // important to return false so the error // placeholder can be placed return false; } @Override public boolean onResourceReady( GlideDrawable resource, String model, Target target, boolean isFromMemoryCache, boolean isFirstResource) { return false; } };

In the onException() method, you can catch the problem and decide what you need to do, for example log it. It’s important that you return false in the onException() method, if Glide should handle the consequences, like displaying an error placeholder itself. You can set the listener we’ve just created on the Glide builder:

Chapter 6 — Exceptions and Debugging

1 2 3 4 5 6

52

Glide .with( context ) .load(UsageExampleListViewAdapter.eatFoodyImages[0]) .listener( requestListener ) .error( R.drawable.cupcake ) .into( imageViewPlaceholder );

The .error() placeholder is not required to make the logging work. However, the R.drawable.cupcake gets only displayed, if you return false in the onException() method of the listener.

Chapter Summary In this chapter, you’ve learned some ways to debug and log exceptions that might occur when working with Glide. Choose one of the ways, depending on your need. Let’s review your learnings: • [x] What options you’ve to debug Glide requests • [x] How to activate logging on local devices • [x] How to use listeners for all app installations Starting with the next chapter, we’ll move towards more advanced topics. Our topic in the following chapter is custom image transformations!

Chapter 7 — Glide Transformations In the previous chapters, you’ve learned all the fundamentals required to utilize the standard capabilities of Glide. Starting with this chapter, and moving forward, we’ll deep dive into a bunch of advanced topics. Next, we’ll take a closer look at transformations.

Transformations Transformations can be used as an image manipulation before the image is being displayed. For example, if your app needs to display an image in grey-scale, but only has access to the original fully-colored version, you could use a transformation to manipulate the bitmap from a happy colored version to a dismal grey version. Don’t understand us wrong, transformations are not limited to colors. You can change pretty much anything of an image: size, bounds, colors, pixel positioning, and more! Glide already ships with two transformations, and we’ve looked at them earlier in image resizing: fitCenter and centerCrop. Both options have such a significance, that they earned their own Glide method, so we won’t include them in this part of the book.

Implementing Your Own Transformation In order to apply your own custom transformations, you’ll need to create a new class, which implements the Transformation interface²⁷. The methods you are required to implement are pretty complex and you’ll have to have quite the insight of the inner architecture of Glide to make it work well. If you just want to transform regular bitmaps for images (no Gifs/videos!), we would recommend to work with the abstract BitmapTransformation²⁸ class. It simplifies the implementation quite a bit and should cover 95% of the use cases. So, let’s take a look at a sample BitmapTransformation implementation. If you read our blog regularly, you know our favorite transformation is blurring images with Renderscript²⁹. We can apply almost the same code we’ve used back then to make it a Glide transformation. Since we have to extend the BitmapTransformation class, we already have our framework:

²⁷http://bumptech.github.io/glide/javadocs/latest/com/bumptech/glide/load/Transformation.html ²⁸http://bumptech.github.io/glide/javadocs/latest/com/bumptech/glide/load/resource/bitmap/BitmapTransformation.html ²⁹https://futurestud.io/blog/how-to-blur-images-efficiently-with-androids-renderscript/

53

Chapter 7 — Glide Transformations

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

public class BlurTransformation extends BitmapTransformation { public BlurTransformation(Context context) { super( context ); } @Override protected Bitmap transform( BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { return null; // todo } @Override public String getId() { return null; // todo } }

Now we fill in our code from the previous blog post to blur an image with Renderscript: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

public class BlurTransformation extends BitmapTransformation { private RenderScript rs; public BlurTransformation(Context context) { super( context ); rs = RenderScript.create( context ); } @Override protected Bitmap transform( BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { Bitmap blurredBitmap = toTransform.copy( Bitmap.Config.ARGB_8888, true );

54

Chapter 7 — Glide Transformations

20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

55

// Allocate memory for Renderscript to work with Allocation input = Allocation.createFromBitmap( rs, blurredBitmap, Allocation.MipmapControl.MIPMAP_FULL, Allocation.USAGE_SHARED ); Allocation output = Allocation.createTyped(rs, input.getType()); // Load up an instance of the specific script that we want to use. ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create( rs, Element.U8_4(rs) ); script.setInput(input); // Set the blur radius script.setRadius(10); // Start the ScriptIntrinisicBlur script.forEach(output); // Copy the output to the blurred bitmap output.copyTo(blurredBitmap); toTransform.recycle(); return blurredBitmap; } @Override public String getId() { return "blur"; } }

Once again, if you’re confused by the code block in transform(), feel free to read up our previous post³⁰. The getId() method describes an unique identifier for this particular transformation. Glide uses that key as part of the caching system. Makes sure you make it unique to avoid unexpected issues. In the next section, we’ll learn how to apply the transformation we’ve just created. ³⁰https://futurestud.io/blog/how-to-blur-images-efficiently-with-androids-renderscript/

Chapter 7 — Glide Transformations

56

Apply a Single Transformation Glide has two ways of applying a transformation. The first is to pass an instance of your class as a parameter to .transform(). You can apply any transformation there, no matter if it’s for an image or Gif. The other option is to use .bitmapTransform(), which only accepts transformations for bitmaps. Since our implementation above is designed for bitmaps, we could use either one: 1 2 3 4 5 6 7

Glide .with( context ) .load( eatFoodyImages[0] ) .transform( new BlurTransformation( context ) ) // this would work too: //.bitmapTransform( new BlurTransformation( context ) ) .into( imageView1 );

This is enough to get Glide to automatically apply our blurring algorithm to the image we’ve loaded from the Internet. Very helpful!

Chapter 7 — Glide Transformations

57

Chapter 7 — Glide Transformations

58

Apply Multiple Transformations Usually, Glide’s fluent interface allows methods to be chained. However, with transformations this is not the case. Make sure you only call .transform() or .bitmapTransform() once, or the previous configuration will be overwritten! Nevertheless, you can still apply multiple transformations by passing multiple transformation objects as the parameter into .transform() (or .bitmapTransform()): 1 2 3 4 5 6 7 8

Glide .with( context ) .load( eatFoodyImages[1] ) .transform( new GreyscaleTransformation( context ), new BlurTransformation( context ) ) .into( imageView2 );

In this code snippet, we apply a grey-scale to the image, and afterwards blur it. Glide executes both transformations automatically for you. Awesome!

Chapter 7 — Glide Transformations

59

Chapter 7 — Glide Transformations

60

Hint: You cannot use .centerCrop() or .fitCenter() when you use transformations. Otherwise your transformation will not be applied!

How to Rotate Your Images A little while ago, we got a question on how to rotate images with Glide, since Picasso offers this functionality out-of-the-box³¹. Unfortunately, Glide does not offer this is one little method call, but in this section we’ll show you how to make it almost as easy. Of course, since this is located in the Glide transformations chapter, we’ll utilize a transformation. Actually, the android.graphics.Matrix³² class offers exactly what we need (and a lot more). The code snippet to rotate an image is really straight-forward: 1 2 3 4 5 6 7

Bitmap toTransform = ... // wherever your bitmap is coming from Matrix matrix = new Matrix(); matrix.postRotate(rotateRotationAngle); Bitmap.createBitmap(toTransform, 0, 0, toTransform.getWidth(), toTransform.getHe\ ight(), matrix, true);

However, in order to make it much more useful to us, especially in the context of using Glide, we’ll wrap this in a BitmapTransformation: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public class RotateTransformation extends BitmapTransformation { private float rotateRotationAngle = 0f; public RotateTransformation(Context context, float rotateRotationAngle) { super( context ); this.rotateRotationAngle = rotateRotationAngle; } @Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth\ , int outHeight) { Matrix matrix = new Matrix(); ³¹https://futurestud.io/blog/picasso-image-rotation-and-transformation ³²http://developer.android.com/reference/android/graphics/Matrix.html

Chapter 7 — Glide Transformations

16 17 18 19 20 21 22 23 24 25 26

61

matrix.postRotate(rotateRotationAngle); return Bitmap.createBitmap(toTransform, 0, 0, toTransform.getWidth(), to\ Transform.getHeight(), matrix, true); } @Override public String getId() { return "rotate" + rotateRotationAngle; } }

Since you’re already familiar with transformation, this section should not raise any questions. So let’s look at a few examples of our new transformation: 1 2 3 4 5 6 7 8 9 10 11 12 13 14

private void loadImageOriginal() { Glide .with( context ) .load( eatFoodyImages[0] ) .into( imageView1 ); } private void loadImageRotated() { Glide .with( context ) .load( eatFoodyImages[0] ) .transform( new RotateTransformation( context, 90f )) .into( imageView3 ); }

Chapter 7 — Glide Transformations

62

Chapter 7 — Glide Transformations

63

Of course, you can change the second parameter on how many degrees the image is going to be rotated to anything you need. You could even set it dynamically! This should provide all the code and knowledge you need to rotate images in Glide, even if it’s not directly provided by the library.

Collection of Glide Transformations If you already have an idea what kind of transformation you might be able to use in your app, take a second to look at the following library: glide-transformations³³. It provides a whole collection of various glide transformations. It’s worth to check if your idea might already be implemented. This extra transformation library ships with two different versions. The extended version includes more transformations, which are computed on the device’s GPU. They require an additional dependency, so the setup for the two versions is a little bit different. You should look through the list of transformations and decide which version you need.

Setup for Glide Transformations The setup is easy! For the basic version you can just add another line to your current build.gradle: 1 2 3

dependencies { compile 'jp.wasabeef:glide-transformations:2.0.0' }

If you would like to use the GPU transformations: 1 2 3 4 5 6 7 8 9

repositories { jcenter() mavenCentral() } dependencies { compile 'jp.wasabeef:glide-transformations:2.0.0' compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.3.0' }

If you want to use the BlurTransformation, you need to do one more step. In case you haven’t already done it, add the following snippet to your build.gradle:

³³https://github.com/wasabeef/glide-transformations

Chapter 7 — Glide Transformations

1 2 3 4 5 6 7 8

64

android { ... defaultConfig { ... renderscriptTargetApi 19 renderscriptSupportModeEnabled true } }

If you want to know about this last step, take a look at our blog post about Renderscript³⁴.

Usage of Glide Transformations After you’ve synced Android Studio with the build.gradle file, you can go ahead and use the transformation collection. The usage pattern is the same as with your own, custom transformations. Let’s assume we would like to blur an image with the collection’s blur transformation: 1 2 3 4 5 6 7 8 9 10 11

Glide .with( context ) .load( eatFoodyImages[2] ) .bitmapTransform( new jp.wasabeef.glide.transformations.BlurTransformation( context, 25, 2 ) ) .into( imageView3 );

You can also apply a list of transformations just like we’ve seen above. The .bitmapTransform() method accepts both, a single transformation or a list of transformations!

Chapter Summary In this chapter, you’ve learned a very useful tool of Glide: transformations. You’ve learned how to implement and apply pre-defined and custom transformations. We hope this gives you everything you need to implement it in your app! In order to make sure you took all the learnings with you, please review them: ³⁴https://futurestud.io/blog/how-to-use-the-renderscript-support-library-with-gradle-based-android-projects/

Chapter 7 — Glide Transformations

• • • • •

65

[x] What are Glide transformations? [x] How to implement your own transformation [x] How to apply one or more of your own transformations [x] How to integrate the transformation library [x] How to apply one or more transformations from the library

After looking at a very customizable feature in this chapter, we’ll continue to do so in the next one. Next, we’ll look at custom animations.

Chapter 8 — Glide Animations In the last chapter, we’ve looked at transforming images before displaying them. We continue in this chapter with the option of animating the display of images.

Animation Basics It’s pretty important to make a smooth transition from image to image. Users appreciate no large unexpected changes popping up in their apps. That’s what Glide animations are for. Glide ships with a standard animation to soften the change in your UI. We’ve looked at .crossFade() in an earlier chapter. But in this chapter, we want to look at other options than the .crossFade(). Glide offers two* options to set an animation. Both versions work with the .animate() method, but pass different parameters. Before we look at code, let us point out that the animations are only used when the image could not be served from a cache. If the image was in a cache, it should be served very quickly and thus the animation isn’t necessary and isn’t displayed. * = We’re ignoring a third, deprecated option: animate(Animation animation).

Animation from Resources Back to the code: the first option is to pass an Android resource id pointing towards an animation resource. A simple example is the slide-in-left animation every Android system offers: android.R.anim.slide_in_left. The code behind it is just an XML description of the animation: 1 2 3 4 5 6 7 8 9 10 11 12



Of course, you can create your own XML animations. For example, a little zoom in animation, which starts the image small and then enlarges it to the full size: 66

Chapter 8 — Glide Animations

1 2 3 4 5 6 7 8 9 10 11 12 13 14

67



We could use either animation to set it in the Glide builder: 1 2 3 4 5

Glide .with( context ) .load( eatFoodyImages[0] ) .animate( android.R.anim.slide_in_left ) // or R.anim.zoom_in .into( imageView1 );

This will slide in the image from the left when the image is loaded from the network and ready to go.

Animation via Custom Class This works well when you’re loading into regular ImageViews. But what if the target is something custom, like we’ve talked about it in a previous section about custom views? Then the other options are quite useful. Instead of passing a reference to an animation resource, you implement a class against the ViewPropertyAnimation.Animator interface. The interface is as simple as it gets, you just need to implement the void animate(View view) method. The view object is the entire target view. If it’s a custom view, you can find the sub elements of your view and do the necessary animations. Let’s look at a simple example. Let’s assume you want to implement a fading animation programmatically, you’d need to create the animator object:

Chapter 8 — Glide Animations

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

68

ViewPropertyAnimation.Animator animationObject = new ViewPropertyAnimation.Anima\ tor() { @Override public void animate(View view) { // if it's a custom view class, cast it here // then find subviews and do the animations // here, we just use the entire view for the fade animation view.setAlpha( 0f ); ObjectAnimator fadeAnim = ObjectAnimator.ofFloat( view, "alpha", 0f, 1f \ ); fadeAnim.setDuration( 2500 ); fadeAnim.start(); } };

Next, you need to set the animation at the Glide request: 1 2 3 4 5

Glide .with( context ) .load( eatFoodyImages[1] ) .animate( animationObject ) .into( imageView2 );

Of course, in the animate(View view) of your animation object method, you can do anything you want to do with the view. Feel free to get creative with your animation. If you’re working with a custom view, you can just cast the view object and then call custom methods on your custom view.

Chapter Summary In this chapter, you’ve learned how to create and apply standard and custom animations to your Glide requests. This is one of the topics where playing around with it is very beneficial. We recommend to take a few minutes to just test our code above and maybe implement one of your own ideas. If you can’t do that right now, at least make sure you understood the following: • • • •

[x] What options you’ve for requesting Glide to show animations [x] How to apply an animation from the Android resources [x] How to implement your own custom animation [x] How to apply your own custom animation

Next, we’ll start to tackle our last big Glide topic block: customizing Glide with Glide modules!

Chapter 9 — Glide Modules After learning about various options for loading and displaying images, we’ll look at making changes to the Glide core. Glide itself connects and controls multiple components, which can be adjusted. In this chapter, we’ll look at various options to customize the core behaviors of Glide. Before we’re going to write a lot of custom code, we’ll start easy by pre-made customizations. Hint: This chapter assumes you’re using Gradle.

Integrating Network Stacks An important piece of displaying images from the Internet is downloading them via HTTP/HTTPS. While the standard Android network packages do their job, there have been quite a few developments for improved networking on Android. Each library has its own advantages and disadvantages. In the end, it comes down to project fit and the developer’s personal taste. The developers of Glide don’t want to force their preferred network library onto you, so Glide is fairly HTTP/S network agnostic. Theoretically, it can work with any implementation, which fulfills basic network capabilities. Integrating a network with Glide is not completely seamless. It requires an interface setup of Glide’s ModelLoader³⁵. In order to make your life easier, Glide provides the implementation for two network libraries: OkHttp³⁶ and Volley³⁷.

OkHttp 2 Let’s assume you want to integrate OkHttp 2 as the network library of your choice for Glide. The integration can be done manually by declaring a GlideModule. If you want to avoid to do the implementation by hand, just open your build.gradle and add the following two lines to your dependencies:

³⁵http://bumptech.github.io/glide/javadocs/latest/com/bumptech/glide/load/model/ModelLoader.html ³⁶https://github.com/square/okhttp ³⁷https://developer.android.com/training/volley/index.html

69

Chapter 9 — Glide Modules

1 2 3 4 5 6 7 8 9 10 11

70

dependencies { // your other dependencies // ... // Glide compile 'com.github.bumptech.glide:glide:3.7.0' // Glide's OkHttp Integration compile 'com.github.bumptech.glide:okhttp-integration:1.4.0@aar' compile 'com.squareup.okhttp:okhttp:2.7.5' }

Gradle will automatically merge the necessary GlideModule into your Android.Manifest. Glide will recognize the existence of it in the manifest and use OkHttp for all network connections.

Volley On the other hand, if you rather use Volley, you have to change your build.gradle dependencies to this: 1 2 3 4 5 6 7 8 9 10 11

dependencies { // your other dependencies // ... // Glide compile 'com.github.bumptech.glide:glide:3.7.0' // Glide's Volley Integration compile 'com.github.bumptech.glide:volley-integration:1.4.0@aar' compile 'com.mcxiaoke.volley:library:1.0.8' }

This will add Volley and the integration library into your project. The integration library adds the GlideModule to your Android.Manifest. Glide will automatically recognize it there and use Volley as the networking library. No further configuration is required! Warning: if you declare both libraries in your build.gradle, both will get added. Since Glide doesn’t load them in any particular order, you’ll be an unstable situation, where it’s not clear which networking library is picked. Make sure you only add one integration library.

OkHttp 3 If you want to use the new OkHttp 3 as the network stack, integrate it via the provided integration library:

Chapter 9 — Glide Modules

1 2 3 4 5 6 7 8 9 10 11

71

dependencies { // your other dependencies // ... // Glide compile 'com.github.bumptech.glide:glide:3.7.0' // Glide's OkHttp3 Integration compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar' compile 'com.squareup.okhttp3:okhttp:3.2.0' }

Other Networking Libraries If you’re a fan of another networking library, you’re out of luck. Glide does not come with an automatic configuration besides Volley and OkHttp. However, feel free to integrate your favorite networking library and open a pull request on GitHub. The implementation for Volley, OkHttp2 and OkHttp3³⁸ might give you a direction. Also, we’ll learn all the necessary steps in the following sections of this chapter. As you can see, integrating the network libraries is fairly easy, if you’re lucky enough to use Gradle as your build system and don’t want any further customization. If you don’t use Gradle, please take a look here³⁹. Next, we’ll take a look at the GlideModule for even more advanced customizations.

Customize Glide with Modules We’ve looked at how you can set various network stacks for Glide to load your images. Internally, the integration libraries for the network stacks are nothing else than declaring a GlideModule, which is a way of significantly customize Glide’s behavior. In this section, we’ll give you an overview over GlideModules. Afterwards, for the rest of the chapter we’ll go into examples of GlideModules.

Glide Modules Glide modules are an abstract way of globally changing the Glide’s behavior. If you need access to the GlideBuilder, which creates the Glide instance you’re working with, this is the way to go. In order to customize Glide, you’ll need to implement a public class against the GlideModule⁴⁰ interface:

³⁸https://github.com/bumptech/glide/tree/3.0/integration ³⁹https://github.com/bumptech/glide/wiki/Integration-Libraries ⁴⁰http://bumptech.github.io/glide/javadocs/latest/com/bumptech/glide/module/GlideModule.html

Chapter 9 — Glide Modules

1 2 3 4 5 6 7 8 9

72

public class SimpleGlideModule implements GlideModule { @Override public void applyOptions(Context context, GlideBuilder builder) { // todo } @Override public void registerComponents(Context context, Glide glide) { // todo } }

The interface offers you two methods to adjust different components of Glide. In this section, we’ll mostly look at the first method: applyOptions(Context context, GlideBuilder builder). So you know you’ll have to create an extra class to customize Glide. Next up is to declare this class globally, so Glide knows that it should load and use it. Glide will scan the AndroidManifest.xml for meta declarations of Glide modules. Thus, you’ve to declare the just created Glide module in the AndroidManifest.xml within the tag: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

View more...

Comments

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF