Создаем приложение для Android — Галерея изображений с Glide
Russian (Pусский) translation by Ellen Nelson (you can also view the original English article)



1. Что такое Glide?
Glide это популярная библиотека Android с открытым исходным кодом для загрузки изображений, видео и анимированных GIF. С Glide можно загружать и отображать медиа из разных источников, например удаленных серверов или из локальной файловой системы.
По умолчанию Glide использует пользовательскую реализацию HttpURLConnection для загрузки изображений через Интернет. Однако Glide также предоставляет плагины для других популярных сетевых библиотек, таких как Volley или OkHttp.
2. Так зачем использовать Glide?
Разработка своего собственного функционала для отображения и загрузки медиа на Java может оказаться настоящей головной болью: вам нужно будет позаботиться о кешировании, декодировании, управлении сетевых соединений, потоков, обработке исключений и о многом другом. Glide — это простая в использовании, хорошо спланированная, хорошо документированная и тщательно протестированная библиотека, которая поможет сохранить вам уйму времени — и избавить вас от головной боли.
В этом уроке, мы будем изучать Glide 3, создавая на нем простую галерею изображений. Изображения будут загружаться из интернета и отображаться миниатюрами в RecyclerView; пользователь нажимает на изображение и открывается детальная активити с изображением побольше.
3. Создаём проект в Android Studio
Запустите Android Studio и создайте новый проект с пустой активити под названием MainActivity.
2. Объявление зависимостей
После создания нового проекта, укажите следующие зависимости в build.gradle.
1 |
repositories { |
2 |
mavenCentral() // jcenter() works as well because it pulls from Maven Central |
3 |
}
|
4 |
|
5 |
dependencies { |
6 |
// Glide
|
7 |
compile 'com.github.bumptech.glide:glide:3.7.0' |
8 |
|
9 |
// Recyclerview
|
10 |
compile 'com.android.support:recyclerview-v7:25.1.1' |
11 |
}
|
12 |
Или с помощью Maven:
1 |
<dependency>
|
2 |
<groupId>com.github.bumptech.glide</groupId> |
3 |
<artifactId>glide</artifactId> |
4 |
<version>3.7.0</version> |
5 |
</dependency>
|
6 |
<dependency>
|
7 |
<groupId>com.google.android</groupId> |
8 |
<artifactId>support-v4</artifactId> |
9 |
<version>r7</version> |
10 |
</dependency>
|
Убедитесь, что вы синхронизации ваш проект после добавления зависимостей Glide.
Интеграция библиотек
Если вы хотите использовать для сетевых операций в вашем проекте такую сетевую библиотеку, как OkHttp или Volley, рекомендуется включить интеграцию Glide для конкретной библиотеки, которую вы используете (вместо той, что по умолчанию, которая завязана на HttpURLConnection).
Volley
1 |
dependencies { |
2 |
|
3 |
compile 'com.github.bumptech.glide:glide:3.7.0' |
4 |
compile 'com.github.bumptech.glide:volley-integration:1.4.0@aar' |
5 |
compile 'com.mcxiaoke.volley:library:1.0.8' |
6 |
}
|
OkHttp
1 |
dependencies { |
2 |
|
3 |
// okhttp 3
|
4 |
compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar' |
5 |
compile 'com.squareup.okhttp3:okhttp:3.2.0' |
6 |
|
7 |
// okhttp 2
|
8 |
compile 'com.github.bumptech.glide:okhttp-integration:1.4.0@aar' |
9 |
compile 'com.squareup.okhttp:okhttp:2.2.0' |
10 |
}
|
Вы можете посетить официальное руководство по интеграции библиотек Glide для получения дополнительной информации.
3. Добавляем разрешение на использование Интернета
Так как Glide выполняет сетевой запрос для загрузки изображений через Интернет, нам нужно включить разрешение на INTERNET в нашем AndroidManifest.xml.
1 |
<uses-permission android:name="android.permission.INTERNET" /> |
4. Создаём макет
Начнем с создания нашего RecyclerView.
1 |
<?xml version="1.0" encoding="utf-8"?>
|
2 |
<RelativeLayout
|
3 |
xmlns:android="http://schemas.android.com/apk/res/android" |
4 |
xmlns:tools="http://schemas.android.com/tools" |
5 |
android:id="@+id/activity_main" |
6 |
android:layout_width="match_parent" |
7 |
android:layout_height="match_parent"> |
8 |
|
9 |
<android.support.v7.widget.RecyclerView
|
10 |
android:id="@+id/rv_images" |
11 |
android:layout_width="match_parent" |
12 |
android:layout_height="match_parent"/> |
13 |
</RelativeLayout>
|
Создание пользовательских элементов макета
Далее создадим структуру XML, которая будет использоваться для каждого элемента (ImageView) внутри RecyclerView.
1 |
<?xml version="1.0" encoding="utf-8"?>
|
2 |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
3 |
android:orientation="vertical" |
4 |
android:layout_width="match_parent" |
5 |
android:layout_height="wrap_content"> |
6 |
<ImageView
|
7 |
android:id="@+id/iv_photo" |
8 |
android:adjustViewBounds="true" |
9 |
android:layout_height="200dp" |
10 |
android:scaleType="centerCrop" |
11 |
android:layout_margin="2dp" |
12 |
android:layout_width="match_parent"/> |
13 |
|
14 |
</LinearLayout>
|
Теперь, когда мы создали макет, следующим шагом будет создание RecyclerView адаптера для заполнения данных. Хотя, прежде чем мы это сделаем, давайте создадим нашу простую модель данных.
5. Создание модели данных
Мы определим простую модель данных для наших RecyclerView. Эта модель реализует Parcelable для высокой производительности передачи данных из одного компонента в другой. В нашем случае данные будут переноситься из SpaceGalleryActivity в SpacePhotoActivity.
1 |
import android.os.Parcel; |
2 |
import android.os.Parcelable; |
3 |
|
4 |
public class SpacePhoto implements Parcelable { |
5 |
|
6 |
private String mUrl; |
7 |
private String mTitle; |
8 |
|
9 |
public SpacePhoto(String url, String title) { |
10 |
mUrl = url; |
11 |
mTitle = title; |
12 |
}
|
13 |
|
14 |
protected SpacePhoto(Parcel in) { |
15 |
mUrl = in.readString(); |
16 |
mTitle = in.readString(); |
17 |
}
|
18 |
|
19 |
public static final Creator<SpacePhoto> CREATOR = new Creator<SpacePhoto>() { |
20 |
@Override
|
21 |
public SpacePhoto createFromParcel(Parcel in) { |
22 |
return new SpacePhoto(in); |
23 |
}
|
24 |
|
25 |
@Override
|
26 |
public SpacePhoto[] newArray(int size) { |
27 |
return new SpacePhoto[size]; |
28 |
}
|
29 |
};
|
30 |
|
31 |
public String getUrl() { |
32 |
return mUrl; |
33 |
}
|
34 |
|
35 |
public void setUrl(String url) { |
36 |
mUrl = url; |
37 |
}
|
38 |
|
39 |
public String getTitle() { |
40 |
return mTitle; |
41 |
}
|
42 |
|
43 |
public void setTitle(String title) { |
44 |
mTitle = title; |
45 |
}
|
46 |
|
47 |
public static SpacePhoto[] getSpacePhotos() { |
48 |
|
49 |
return new SpacePhoto[]{ |
50 |
new SpacePhoto("http://i.imgur.com/zuG2bGQ.jpg", "Galaxy"), |
51 |
new SpacePhoto("http://i.imgur.com/ovr0NAF.jpg", "Space Shuttle"), |
52 |
new SpacePhoto("http://i.imgur.com/n6RfJX2.jpg", "Galaxy Orion"), |
53 |
new SpacePhoto("http://i.imgur.com/qpr5LR2.jpg", "Earth"), |
54 |
new SpacePhoto("http://i.imgur.com/pSHXfu5.jpg", "Astronaut"), |
55 |
new SpacePhoto("http://i.imgur.com/3wQcZeY.jpg", "Satellite"), |
56 |
};
|
57 |
}
|
58 |
|
59 |
@Override
|
60 |
public int describeContents() { |
61 |
return 0; |
62 |
}
|
63 |
|
64 |
@Override
|
65 |
public void writeToParcel(Parcel parcel, int i) { |
66 |
parcel.writeString(mUrl); |
67 |
parcel.writeString(mTitle); |
68 |
}
|
69 |
}
|
6. Создаем адаптер
Создадим адаптер для заполнения RecyclerView данными. Также реализуем прослушиватель нажатия, чтобы открывалась активити с детализацией — SpacePhotoActivity, передавая экземпляр SpacePhoto в дополнение. Актвити детализации будет показывать изображение крупным планом. Мы создадим его в следующем разделе.
1 |
public class MainActivity extends AppCompatActivity { |
2 |
// ...
|
3 |
private class ImageGalleryAdapter extends RecyclerView.Adapter<ImageGalleryAdapter.MyViewHolder> { |
4 |
|
5 |
@Override
|
6 |
public ImageGalleryAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { |
7 |
|
8 |
Context context = parent.getContext(); |
9 |
LayoutInflater inflater = LayoutInflater.from(context); |
10 |
View photoView = inflater.inflate(R.layout.item_photo, parent, false); |
11 |
ImageGalleryAdapter.MyViewHolder viewHolder = new ImageGalleryAdapter.MyViewHolder(photoView); |
12 |
return viewHolder; |
13 |
}
|
14 |
|
15 |
@Override
|
16 |
public void onBindViewHolder(ImageGalleryAdapter.MyViewHolder holder, int position) { |
17 |
|
18 |
SpacePhoto spacePhoto = mSpacePhotos[position]; |
19 |
ImageView imageView = holder.mPhotoImageView; |
20 |
}
|
21 |
|
22 |
@Override
|
23 |
public int getItemCount() { |
24 |
return (mSpacePhotos.length); |
25 |
}
|
26 |
|
27 |
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { |
28 |
|
29 |
public ImageView mPhotoImageView; |
30 |
|
31 |
public MyViewHolder(View itemView) { |
32 |
|
33 |
super(itemView); |
34 |
mPhotoImageView = (ImageView) itemView.findViewById(R.id.iv_photo); |
35 |
itemView.setOnClickListener(this); |
36 |
}
|
37 |
|
38 |
@Override
|
39 |
public void onClick(View view) { |
40 |
|
41 |
int position = getAdapterPosition(); |
42 |
if(position != RecyclerView.NO_POSITION) { |
43 |
SpacePhoto spacePhoto = mSpacePhotos[position]; |
44 |
Intent intent = new Intent(mContext, SpacePhotoActivity.class); |
45 |
intent.putExtra(SpacePhotoActivity.EXTRA_SPACE_PHOTO, spacePhoto); |
46 |
startActivity(intent); |
47 |
}
|
48 |
}
|
49 |
}
|
50 |
|
51 |
private SpacePhoto[] mSpacePhotos; |
52 |
private Context mContext; |
53 |
|
54 |
public ImageGalleryAdapter(Context context, SpacePhoto[] spacePhotos) { |
55 |
mContext = context; |
56 |
mSpacePhotos = spacePhotos; |
57 |
}
|
58 |
}
|
59 |
}
|
7. Загрузка изображения по URL
Вот тут нам и понадобится Glide, чтобы получить изображения из Интернета и отобразить их в отдельных ImageView, используя наш метод onBindViewHolder() в RecyclerView, когда пользователь прокручивает содержимое приложения.
1 |
// ...
|
2 |
@Override
|
3 |
public void onBindViewHolder(MyViewHolder holder, int position) { |
4 |
Photo photo = mPhotoList.get(position); |
5 |
ImageView imageView = holder.mPhotoImageView; |
6 |
|
7 |
Glide.with(mContext) |
8 |
.load(spacePhoto.getUrl()) |
9 |
.placeholder(R.drawable.ic_cloud_off_red) |
10 |
.into(imageView); |
11 |
}
|
12 |
// ...
|
Шаг за шагом, как мы будем применять Glide:
-
with(Context context): мы начинаем процесс загрузки с передачи нашего первого содержимого в методwith(). -
load(String string): источник изображения указан либо как путь к каталогу, URI или URL адреса. -
placeholder(int resourceId): идентификатор локального ресурса приложения, желательно использовать "drawable", то что будет показано пока не загрузится и отобразится нужное изображение.
-
into(ImageView imageView): представление изображения, куда будет помещено настоящее изображение.
Помните, что Glide также может загружать локальные изображения, по идентификатору Android ресурса, по пути к файлу или Uri в качестве аргумента метода load().
Изменение размера изображения и преобразование
Вы можете изменить размер изображения, прежде чем оно отобразится в ImageView с методом Glide .override(int width, int height). Это полезно если ваше приложение создает превьюшки, при загрузке изображения с сервера с другими размерами. Обратите внимание, что размеры в пикселях, а не dp.
Также доступны следующие преобразования изображения:
-
fitCenter(): масштабирует изображение равномерно (сохраняя пропорции изображения) так, чтобы изображение вместилось в данную область. Изображение будет видно целиком и может быть иметь вертикальный или горизонтальный отступ. -
centerCrop(): масштабирует изображение равномерно (сохраняя пропорции изображения) так, чтобы изображение заполняло данную область, показав изображение по возможности полностью. При необходимости, изображение будет обрезано по горизонтали или вертикали, для соответствия размеру.
8. Инициализация адаптера
Теперь создадим RecyclerView с GridLayoutManager как менеджер компоновки, инициализируем адаптер и привяжем его к RecyclerView.
1 |
// ...
|
2 |
@Override
|
3 |
protected void onCreate(Bundle savedInstanceState) { |
4 |
super.onCreate(savedInstanceState); |
5 |
setContentView(R.layout.activity_main); |
6 |
|
7 |
RecyclerView.LayoutManager layoutManager = new GridLayoutManager(this, 2); |
8 |
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv_images); |
9 |
recyclerView.setHasFixedSize(true); |
10 |
recyclerView.setLayoutManager(layoutManager); |
11 |
|
12 |
ImageGalleryAdapter adapter = new ImageGalleryAdapter(this, SpacePhoto.getSpacePhotos()); |
13 |
recyclerView.setAdapter(adapter); |
14 |
|
15 |
}
|
16 |
// ...
|
9. Создание активити для детализации
Создадим новую активити и назовем ее SpacePhotoActivity. Мы получим SpacePhoto и загрузку изображений с Glide, как мы делали это ранее. Здесь мы ожидаем файл или URL для Bitmap, поэтому мы будем использовать asBitmap(), для того, чтобы Glide получил Bitmap. В противном случае загрузка завершится ошибкой и сработает вызов .error() — тогда будет возвращен и показан ресурс drawable из вызванной ошибки. Также можно использовать asGif(), если вы хотите проверить, что загружаемые изображения являются GIF. (Я вскоре объясню, как работает GIF в Glide).
1 |
import android.graphics.Bitmap; |
2 |
import android.graphics.Color; |
3 |
import android.os.Bundle; |
4 |
import android.support.v7.app.AppCompatActivity; |
5 |
import android.support.v7.graphics.Palette; |
6 |
import android.view.ViewGroup; |
7 |
import android.widget.ImageView; |
8 |
import com.bumptech.glide.Glide; |
9 |
import com.bumptech.glide.request.RequestListener; |
10 |
import com.bumptech.glide.request.target.Target; |
11 |
|
12 |
public class SpacePhotoActivity extends AppCompatActivity { |
13 |
|
14 |
public static final String EXTRA_SPACE_PHOTO = "SpacePhotoActivity.SPACE_PHOTO"; |
15 |
private ImageView mImageView; |
16 |
|
17 |
@Override
|
18 |
protected void onCreate(Bundle savedInstanceState) { |
19 |
super.onCreate(savedInstanceState); |
20 |
setContentView(R.layout.activity_photo_detail); |
21 |
|
22 |
mImageView = (ImageView) findViewById(R.id.image); |
23 |
SpacePhoto spacePhoto = getIntent().getParcelableExtra(EXTRA_SPACE_PHOTO); |
24 |
|
25 |
Glide.with(this) |
26 |
.load(spacePhoto.getUrl()) |
27 |
.asBitmap() |
28 |
.error(R.drawable.ic_cloud_off_red) |
29 |
.diskCacheStrategy(DiskCacheStrategy.SOURCE) |
30 |
.into(mImageView); |
31 |
}
|
32 |
}
|
Обратите внимание, что мы также инициализируем уникальный кэш для загрузки изображений: DiskCacheStrategy.SOURCE. Я расскажу подробнее о кэшировании в следующем разделе.
Макет детального вида
Вот макет для отображения активити детализации. Он просто отображает прокручиваемый ImageView, который будет показывать полноразмерное изображение.
1 |
<?xml version="1.0" encoding="utf-8"?>
|
2 |
<LinearLayout
|
3 |
xmlns:android="http://schemas.android.com/apk/res/android" |
4 |
android:layout_width="match_parent" |
5 |
android:layout_height="match_parent" |
6 |
android:orientation="vertical"> |
7 |
|
8 |
<ScrollView
|
9 |
android:layout_width="match_parent" |
10 |
android:layout_height="match_parent"> |
11 |
|
12 |
<LinearLayout
|
13 |
android:id="@+id/activity_character" |
14 |
android:layout_width="match_parent" |
15 |
android:layout_height="wrap_content" |
16 |
android:orientation="vertical" |
17 |
android:layout_gravity="center_vertical"> |
18 |
|
19 |
<ImageView
|
20 |
android:id="@+id/image" |
21 |
android:layout_width="match_parent" |
22 |
android:layout_height="wrap_content" |
23 |
android:adjustViewBounds="true" |
24 |
android:scaleType="fitCenter"/> |
25 |
|
26 |
</LinearLayout>
|
27 |
</ScrollView>
|
28 |
</LinearLayout>
|
10. Кэширование в Glide
Если посмотреть внимательно, вы увидите, что когда вы возвращаетесь к изображению, которое было загружено ранее, оно загружается быстрее, чем раньше. Так почему быстрее? Система кэширования Glide, вот почему.
Как только изображение было загружено с интернета, Glide сохранит его в памяти и на диске, сохранив от повтора сетевых запросов и позволив быстрее получить изображение. Таким образом, перед загрузкой изображения из сети, Glide сначала проверяет наличие изображения в памяти или на диске.
В зависимости от вашего приложения, вы можете пожелать отказаться от кеширования — например, если отображаемые изображения будут часто изменяться.
Как отключить кэширование?
Вы можете отказаться от кэширования в памяти путем вызова .skipMemoryCache(true). Но имейте в виду, что изображения по-прежнему будут кэшироваться на диске — для предотвращения этого — используется метод .diskCacheStrategy (DiskCacheStrategy strategy), который принимает одно из следующих значений:
-
DiskCacheStrategy.NONE: данные не сохраняется в кэше. -
DiskCacheStrategy.SOURCE: исходные данные сохраняются в кэше. -
DiskCacheStrategy.RESULT: сохраняет результат данных после преобразований в кэше. -
DiskCacheStrategy.ALL: кэширует исходные и преобразованные данные.
Чтобы отключить оба кеширования и в памяти и на диск, просто вызовите оба метода один за другим:
1 |
Glide.with(this) |
2 |
.load(spacePhoto.getUrl()) |
3 |
.asBitmap() |
4 |
.skipMemoryCache(true) |
5 |
.diskCacheStrategy(DiskCacheStrategy.NONE) |
6 |
.into(imageView); |
11. Прослушивание запросов
В Glide можно применить RequestListener для отслеживания состояний запроса, сделанного при загрузке изображения. Будет вызван только один из методов.
-
onException(): срабатывает всякий раз, когда возникает исключение, так что вы можете обрабатывать исключения в этом методе. -
onResourceReady(): срабатывает, когда изображение загрузилось успешно.
Вернемся к нашему приложению галереи изображений и немного изменим отображение, с помощью объекта RequestListener, который будет назначать растровое изображение для ImageView, а также изменим фоновый цвет путем извлечения темного оттенка, в зависимости от цвета изображения с помощью API палитры Android.
1 |
// ...
|
2 |
@Override
|
3 |
protected void onCreate(Bundle savedInstanceState) { |
4 |
// ...
|
5 |
Glide.with(this) |
6 |
.load(spacePhoto.getUrl()) |
7 |
.asBitmap() |
8 |
.error(R.drawable.ic_cloud_off_red) |
9 |
.listener(new RequestListener<String, Bitmap>() { |
10 |
|
11 |
@Override
|
12 |
public boolean onException(Exception e, String model, Target<Bitmap> target, boolean isFirstResource) { |
13 |
return false; |
14 |
}
|
15 |
|
16 |
@Override
|
17 |
public boolean onResourceReady(Bitmap resource, String model, Target<Bitmap> target, boolean isFromMemoryCache, boolean isFirstResource) { |
18 |
|
19 |
onPalette(Palette.from(resource).generate()); |
20 |
mImageView.setImageBitmap(resource); |
21 |
|
22 |
return false; |
23 |
}
|
24 |
|
25 |
public void onPalette(Palette palette) { |
26 |
if (null != palette) { |
27 |
ViewGroup parent = (ViewGroup) mImageView.getParent().getParent(); |
28 |
parent.setBackgroundColor(palette.getDarkVibrantColor(Color.GRAY)); |
29 |
}
|
30 |
}
|
31 |
})
|
32 |
.diskCacheStrategy(DiskCacheStrategy.SOURCE) |
33 |
.into(mImageView); |
34 |
}
|
35 |
// ...
|
Здесь можно также скрыть диалоговое окно прогресса, если оно у вас есть. С этим последним измененим не забудьте включить зависимость от Палитры в ваш build.gradle:
1 |
dependencies { |
2 |
// ...
|
3 |
compile 'com.android.support:palette-v7:25.1.1' |
4 |
}
|
12. Тестируем приложения
Наконец, можно запустить приложение! Нажмите на превьюшку, чтобы полуить полноразмерное изображение.



10. Анимации
Когда вы запустите приложение, вы заметите crossfade анимацию при отображении изображения. Это по умолчанию включено в Glide, но вы можете отключить его путем вызова dontAnimate(), так что изображение будет просто отображаться без каких-либо анимации. Вы также можете изменить эту анимацию затухания путем вызова crossFade(int duration), указав продолжительность в миллисекундах, либо ускорить ее или замедлить, значение по умолчанию — 300 миллисекунд.
Анимированные GIF
Показывать анимированные GIF в вашем приложении через Glide очень просто. Это работает так же, как отображение обычного изображения.
1 |
ImageView gifImageView = (ImageView) findViewById(R.id.iv_gif); |
2 |
|
3 |
Glide.with(this) |
4 |
.load("http://i.imgur.com/Vth6CBz.gif") |
5 |
.asGif() |
6 |
.placeholder(R.drawable.ic_cloud_off_red) |
7 |
.error(R.drawable.ic_cloud_off_red) |
8 |
.into(gifImageView); |
Если вы ожидаете GIF изображение, вызовите asGif() — это настроит Glide на получение GIF изображения, в противном случае загрузка завершится ошибкой и вместо этого будет показан Drawable, переданный методом .error().
Воспроизведение видео
К сожалению Glide не поддерживает загрузку и отображение видео через URL. Вместо этого, он может только загружать и отображать видео с телефона. Показать видео можно передав его URI в метод load().
1 |
Glide.with(context) |
2 |
.load(Uri.fromFile(new File("your/video/file/path")) |
3 |
.into(imageView) |
Заключение
Отличная работа! На этом уроке вы построили полноценное приложении галереи с Glide и по пути узнали как работает эта библиотека и как вы можете интегрировать ее в свой собственный проект. Вы также узнали, как отображать локальные и удаленные изображения, как показывать анимированные GIF-файлы и видео, как применять преобразования изображения, на примере изменения размеров. И не только это. Вы видели, как легко можно включить кэширование, обработку ошибок и прослушиватьель запросов.
Чтобы узнать больше о Glide, обратитесь к ее официальной документации. Чтобы узнать больше о написании приложений для Android, посмотрите некоторые из наших других курсов и уроков на Envato Tuts+!


Android SDKКак совершать звонки и использовать SMS в Android приложенияхChike Mgbemena

Android SDKAndroid штучки: Ваш первый проектPaul Trebilcox-Ruiz

Android SDKСоздание просмотрщика видео с Cardboard 360 для AndroidPaul Trebilcox-Ruiz

Android SDKДелаем снимки вашим приложением для AndroidAshraff Hathibelagal



