Статьи

Кодирование Android-галереи изображений с помощью Glide

Конечный продукт
Что вы будете создавать

Glide — это популярная библиотека Android с открытым исходным кодом для загрузки изображений, видео и анимированных GIF-файлов. С Glide вы можете загружать и отображать мультимедиа из разных источников, таких как удаленные серверы или локальная файловая система.

По умолчанию Glide использует пользовательскую реализацию HttpURLConnection для загрузки изображений через Интернет. Тем не менее, Glide также предоставляет плагины для других популярных сетевых библиотек, таких как Volley или OkHttp .

Разработка собственных функций загрузки и отображения мультимедиа в Java может быть реальной проблемой: вам нужно позаботиться о кэшировании, декодировании, управлении сетевыми подключениями, потоке, обработке исключений и многом другом. Glide — это простая в использовании, хорошо спланированная, хорошо документированная и тщательно протестированная библиотека, которая может сэкономить вам много драгоценного времени и избавить вас от головной боли.

В этом уроке мы узнаем о Glide 3, создав простое приложение для создания галереи изображений. Он загружает изображения через Интернет и отображает их в виде миниатюр в RecyclerView , а когда пользователь нажимает на любое изображение, он открывает детальное действие, содержащее большее изображение.

MainActivity свою Android Studio и создайте новый проект с пустым действием под названием MainActivity .

Скриншот нового проекта Android Studio

После создания нового проекта укажите следующие зависимости в вашем build.gradle .

01
02
03
04
05
06
07
08
09
10
11
repositories {
  mavenCentral() // jcenter() works as well because it pulls from Maven Central
}
 
dependencies {
    // Glide
    compile ‘com.github.bumptech.glide:glide:3.7.0’
     
    // Recyclerview
    compile ‘com.android.support:recyclerview-v7:25.1.1’
}

Или с Maven:

01
02
03
04
05
06
07
08
09
10
<dependency>
  <groupId>com.github.bumptech.glide</groupId>
  <artifactId>glide</artifactId>
  <version>3.7.0</version>
</dependency>
<dependency>
  <groupId>com.google.android</groupId>
  <artifactId>support-v4</artifactId>
  <version>r7</version>
</dependency>

Убедитесь, что вы синхронизируете свой проект после добавления зависимости Glide.

Если вы хотите использовать сетевую библиотеку, такую ​​как OkHttp или Volley, в своем проекте для сетевых операций, рекомендуется включить специальную интеграцию Glide для используемой вами библиотеки (вместо стандартной по умолчанию, которая объединяет HttpURLConnection ).

1
2
3
4
5
6
dependencies {
 
    compile ‘com.github.bumptech.glide:glide:3.7.0’
    compile ‘com.github.bumptech.glide:volley-integration:1.4.0@aar’
    compile ‘com.mcxiaoke.volley:library:1.0.8’
}
01
02
03
04
05
06
07
08
09
10
dependencies {
       
    // okhttp 3
    compile ‘com.github.bumptech.glide:okhttp3-integration:1.4.0@aar’
    compile ‘com.squareup.okhttp3:okhttp:3.2.0’
     
    // okhttp 2
    compile ‘com.github.bumptech.glide:okhttp-integration:1.4.0@aar’
    compile ‘com.squareup.okhttp:okhttp:2.2.0’
}

Вы можете посетить официальное руководство по интеграции библиотек Glide для получения дополнительной информации.

Поскольку Glide собирается выполнить сетевой запрос на загрузку изображений через Интернет, нам нужно включить разрешение INTERNET в наш AndroidManifest.xml .

1
<uses-permission android:name=»android.permission.INTERNET» />

Мы начнем с создания нашего RecyclerView .

01
02
03
04
05
06
07
08
09
10
11
12
13
<?xml version=»1.0″ encoding=»utf-8″?>
<RelativeLayout
        xmlns:android=»http://schemas.android.com/apk/res/android»
        xmlns:tools=»http://schemas.android.com/tools»
        android:id=»@+id/activity_main»
        android:layout_width=»match_parent»
        android:layout_height=»match_parent»>
 
    <android.support.v7.widget.RecyclerView
            android:id=»@+id/rv_images»
            android:layout_width=»match_parent»
            android:layout_height=»match_parent»/>
</RelativeLayout>

Далее, давайте создадим макет XML, который будет использоваться для каждого элемента ( ImageView ) в RecyclerView .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
<?xml version=»1.0″ encoding=»utf-8″?>
<LinearLayout xmlns:android=»http://schemas.android.com/apk/res/android»
              android:orientation=»vertical»
              android:layout_width=»match_parent»
              android:layout_height=»wrap_content»>
<ImageView
        android:id=»@+id/iv_photo»
        android:adjustViewBounds=»true»
        android:layout_height=»200dp»
        android:scaleType=»centerCrop»
        android:layout_margin=»2dp»
        android:layout_width=»match_parent»/>
 
</LinearLayout>

Теперь, когда мы создали макет, следующим шагом будет создание адаптера RecyclerView для RecyclerView данными. Прежде чем мы сделаем это, давайте создадим нашу простую модель данных.

Мы собираемся определить простую модель данных для нашего RecyclerView . Эта модель реализует Parcelable для высокопроизводительной передачи данных из одного компонента в другой. В нашем случае данные будут перенесены из SpaceGalleryActivity в SpacePhotoActivity .

01
02
03
04
05
06
07
08
09
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import android.os.Parcel;
import android.os.Parcelable;
 
public class SpacePhoto implements Parcelable {
 
    private String mUrl;
    private String mTitle;
 
    public SpacePhoto(String url, String title) {
        mUrl = url;
        mTitle = title;
    }
 
    protected SpacePhoto(Parcel in) {
        mUrl = in.readString();
        mTitle = in.readString();
    }
 
    public static final Creator<SpacePhoto> CREATOR = new Creator<SpacePhoto>() {
        @Override
        public SpacePhoto createFromParcel(Parcel in) {
            return new SpacePhoto(in);
        }
 
        @Override
        public SpacePhoto[] newArray(int size) {
            return new SpacePhoto[size];
        }
    };
 
    public String getUrl() {
        return mUrl;
    }
 
    public void setUrl(String url) {
        mUrl = url;
    }
 
    public String getTitle() {
        return mTitle;
    }
 
    public void setTitle(String title) {
        mTitle = title;
    }
 
    public static SpacePhoto[] getSpacePhotos() {
 
        return new SpacePhoto[]{
                new SpacePhoto(«http://i.imgur.com/zuG2bGQ.jpg», «Galaxy»),
                new SpacePhoto(«http://i.imgur.com/ovr0NAF.jpg», «Space Shuttle»),
                new SpacePhoto(«http://i.imgur.com/n6RfJX2.jpg», «Galaxy Orion»),
                new SpacePhoto(«http://i.imgur.com/qpr5LR2.jpg», «Earth»),
                new SpacePhoto(«http://i.imgur.com/pSHXfu5.jpg», «Astronaut»),
                new SpacePhoto(«http://i.imgur.com/3wQcZeY.jpg», «Satellite»),
        };
    }
 
    @Override
    public int describeContents() {
        return 0;
    }
 
    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeString(mUrl);
        parcel.writeString(mTitle);
    }
}

Мы создадим адаптер для заполнения RecyclerView данными. Мы также реализуем прослушиватель SpacePhotoActivity чтобы открыть детальное действие — SpacePhotoActivity передав ему экземпляр SpacePhoto в качестве дополнительного. Детальное действие покажет крупный план изображения. Мы создадим его в следующем разделе.

01
02
03
04
05
06
07
08
09
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class MainActivity extends AppCompatActivity {
    // …
    private class ImageGalleryAdapter extends RecyclerView.Adapter<ImageGalleryAdapter.MyViewHolder> {
 
        @Override
        public ImageGalleryAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
 
            Context context = parent.getContext();
            LayoutInflater inflater = LayoutInflater.from(context);
            View photoView = inflater.inflate(R.layout.item_photo, parent, false);
            ImageGalleryAdapter.MyViewHolder viewHolder = new ImageGalleryAdapter.MyViewHolder(photoView);
            return viewHolder;
        }
 
        @Override
        public void onBindViewHolder(ImageGalleryAdapter.MyViewHolder holder, int position) {
 
            SpacePhoto spacePhoto = mSpacePhotos[position];
            ImageView imageView = holder.mPhotoImageView;
        }
 
        @Override
        public int getItemCount() {
            return (mSpacePhotos.length);
        }
 
        public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
 
            public ImageView mPhotoImageView;
 
            public MyViewHolder(View itemView) {
 
                super(itemView);
                mPhotoImageView = (ImageView) itemView.findViewById(R.id.iv_photo);
                itemView.setOnClickListener(this);
            }
 
            @Override
            public void onClick(View view) {
 
                int position = getAdapterPosition();
                if(position != RecyclerView.NO_POSITION) {
                    SpacePhoto spacePhoto = mSpacePhotos[position];
                    Intent intent = new Intent(mContext, SpacePhotoActivity.class);
                    intent.putExtra(SpacePhotoActivity.EXTRA_SPACE_PHOTO, spacePhoto);
                    startActivity(intent);
                }
            }
        }
 
        private SpacePhoto[] mSpacePhotos;
        private Context mContext;
 
        public ImageGalleryAdapter(Context context, SpacePhoto[] spacePhotos) {
            mContext = context;
            mSpacePhotos = spacePhotos;
        }
    }
}

Именно здесь нам нужно, чтобы Glide выполнял свою работу — извлекать изображения из Интернета и отображать их в отдельных ImageView , используя наш метод RecyclerView onBindViewHolder() когда пользователь прокручивает приложение.

01
02
03
04
05
06
07
08
09
10
11
12
// …
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
    Photo photo = mPhotoList.get(position);
    ImageView imageView = holder.mPhotoImageView;
 
     Glide.with(mContext)
            .load(spacePhoto.getUrl())
            .placeholder(R.drawable.ic_cloud_off_red)
            .into(imageView);
}
// …

Шаг за шагом, вот что делают вызовы Glide:

  • with(Context context) : мы запускаем процесс загрузки, сначала передавая наш контекст в метод with() .
  • load(String string) : источник изображения указывается в виде пути к каталогу, URI или URL-адреса.
  • placeholder(int resourceId) : локальный идентификатор ресурса приложения, предпочтительно для рисования, который будет заполнителем до тех пор, пока изображение не будет загружено и отображено.
  • into(ImageView imageView) : представление целевого изображения, в которое будет помещено изображение.

Помните, что Glide также может загружать локальные изображения. Просто передайте идентификатор ресурса Android, путь к файлу или URI в качестве аргумента методу load() .

Вы можете изменить размер изображения до его отображения в ImageView с помощью .override(int width, int height) Glide .override(int width, int height) . Это полезно для создания миниатюр в вашем приложении при загрузке другого размера изображения с сервера. Обратите внимание, что размеры указаны в пикселях, а не в dp.

Доступны также следующие преобразования изображений:

  • fitCenter() : s равномерно запечатывает изображение (с сохранением соотношения сторон изображения), чтобы изображение помещалось в заданную область. Все изображение будет видимым, но может быть вертикальное или горизонтальное заполнение.
  • centerCrop() : равномерно масштабирует изображение (поддерживая соотношение сторон изображения), так что изображение заполняет заданную область, с максимально возможным отображением изображения. При необходимости изображение будет обрезано по горизонтали или вертикали для соответствия.

Здесь мы создаем наш RecyclerView с GridLayoutManager в качестве менеджера макета, инициализируем наш адаптер и привязываем его к RecyclerView .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
// …
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
 
    RecyclerView.LayoutManager layoutManager = new GridLayoutManager(this, 2);
    RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv_images);
    recyclerView.setHasFixedSize(true);
    recyclerView.setLayoutManager(layoutManager);
 
    ImageGalleryAdapter adapter = new ImageGalleryAdapter(this, SpacePhoto.getSpacePhotos());
    recyclerView.setAdapter(adapter);
 
}
// …

Создайте новое действие и назовите его SpacePhotoActivity . Мы получаем дополнительный SpacePhoto и загружаем изображение с Glide, как и раньше. Здесь мы ожидаем, что файл или URL будет Bitmap , поэтому мы будем использовать asBitmap() чтобы Glide получал Bitmap . В противном случае загрузка завершится неудачно, и будет вызван обратный вызов .error() результате чего будет отображен извлекаемый ресурс, возвращенный из обратного вызова ошибки.

Вы также можете использовать asGif() если хотите, чтобы загруженное изображение было в формате GIF. (Вскоре я объясню, как GIF-файлы работают в Glide.)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.graphics.Palette;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
 
public class SpacePhotoActivity extends AppCompatActivity {
 
    public static final String EXTRA_SPACE_PHOTO = «SpacePhotoActivity.SPACE_PHOTO»;
    private ImageView mImageView;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_photo_detail);
 
        mImageView = (ImageView) findViewById(R.id.image);
        SpacePhoto spacePhoto = getIntent().getParcelableExtra(EXTRA_SPACE_PHOTO);
         
        Glide.with(this)
                .load(spacePhoto.getUrl())
                .asBitmap()
                .error(R.drawable.ic_cloud_off_red)
                .diskCacheStrategy(DiskCacheStrategy.SOURCE)
                .into(mImageView);
    }
}

Обратите внимание, что мы также инициализировали уникальный кеш для загруженных изображений: DiskCacheStrategy.SOURCE . Я объясню больше о кешировании в следующем разделе.

Вот макет для отображения детализации деятельности. Он просто отображает прокручиваемый ImageView , который покажет версию загруженного изображения в полном разрешении.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version=»1.0″ encoding=»utf-8″?>
<LinearLayout
        xmlns:android=»http://schemas.android.com/apk/res/android»
        android:layout_width=»match_parent»
        android:layout_height=»match_parent»
        android:orientation=»vertical»>
 
    <ScrollView
            android:layout_width=»match_parent»
            android:layout_height=»match_parent»>
 
        <LinearLayout
                android:id=»@+id/activity_character»
                android:layout_width=»match_parent»
                android:layout_height=»wrap_content»
                android:orientation=»vertical»
                android:layout_gravity=»center_vertical»>
 
            <ImageView
                    android:id=»@+id/image»
                    android:layout_width=»match_parent»
                    android:layout_height=»wrap_content»
                    android:adjustViewBounds=»true»
                    android:scaleType=»fitCenter»/>
             
        </LinearLayout>
    </ScrollView>
</LinearLayout>

Если вы внимательно посмотрите, вы увидите, что при повторном посещении ранее загруженного изображения оно загружается даже быстрее, чем раньше. Что сделало это быстрее? Система кеширования Glide, вот что.

После того, как изображение было загружено один раз из Интернета, Glide будет кэшировать его в памяти и на диске, сохраняя повторные сетевые запросы и позволяя быстрее получать изображение. Итак, Glide сначала проверит, доступно ли изображение в памяти или на диске, прежде чем загружать его из сети.

Однако в зависимости от вашего приложения вы можете избежать кэширования, например, если отображаемые изображения могут часто меняться и не будут перезагружаться.

Вы можете избежать кэширования памяти, вызвав .skipMemoryCache(true) . Но имейте в виду, что образ по-прежнему будет кэшироваться на диске — чтобы предотвратить это, вы также используете метод .diskCacheStrategy(DiskCacheStrategy strategy) , который принимает одно из следующих значений перечисления:

  • DiskCacheStrategy.NONE : данные не сохраняются в кэш.
  • DiskCacheStrategy.SOURCE : исходные данные, сохраненные в кеше.
  • DiskCacheStrategy.RESULT : сохраняет результаты данных после преобразований в кэш.
  • DiskCacheStrategy.ALL : кэширует как исходные, так и преобразованные данные.

Чтобы полностью избежать кеширования памяти и диска, просто вызовите оба метода один за другим:

1
2
3
4
5
6
Glide.with(this)
       .load(spacePhoto.getUrl())
       .asBitmap()
       .skipMemoryCache(true)
       .diskCacheStrategy(DiskCacheStrategy.NONE)
       .into(imageView);

В Glide вы можете реализовать RequestListener для мониторинга состояния запроса, который вы сделали при загрузке изображения. Только один из этих методов будет вызван.

  • onException() : срабатывает всякий раз, когда возникает исключение, поэтому вы можете обрабатывать исключения в этом методе.
  • onResourceReady() : срабатывает при успешной загрузке изображения.

Возвращаясь к нашему приложению галереи изображений, давайте немного изменим отображение, используя объект RequestListener , который установит растровое изображение в ImageView а также изменим цвет фона макета, извлекая темный и вибрирующий цвет нашего изображения с помощью палитры Android. API .

01
02
03
04
05
06
07
08
09
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
// …
@Override
protected void onCreate(Bundle savedInstanceState) {
    // …
    Glide.with(this)
            .load(spacePhoto.getUrl())
            .asBitmap()
            .error(R.drawable.ic_cloud_off_red)
            .listener(new RequestListener<String, Bitmap>() {
     
                @Override
                public boolean onException(Exception e, String model, Target<Bitmap> target, boolean isFirstResource) {
                    return false;
                }
     
                @Override
                public boolean onResourceReady(Bitmap resource, String model, Target<Bitmap> target, boolean isFromMemoryCache, boolean isFirstResource) {
     
                    onPalette(Palette.from(resource).generate());
                    mImageView.setImageBitmap(resource);
     
                    return false;
                }
     
                public void onPalette(Palette palette) {
                    if (null != palette) {
                        ViewGroup parent = (ViewGroup) mImageView.getParent().getParent();
                        parent.setBackgroundColor(palette.getDarkVibrantColor(Color.GRAY));
                    }
                }
            })
            .diskCacheStrategy(DiskCacheStrategy.SOURCE)
            .into(mImageView);
}
// …

Здесь вы также можете скрыть диалог прогресса, если он у вас есть. С этим последним изменением обязательно build.gradle зависимость Palette в ваш build.gradle :

1
2
3
4
dependencies {
    // …
    compile ‘com.android.support:palette-v7:25.1.1’
}

Наконец, вы можете запустить приложение! Нажмите на миниатюру, чтобы получить полноразмерную версию изображения.

Эмулятор запуска приложения скриншоты

Если вы запустите приложение, вы заметите перекрестную анимацию, которая появляется во время отображения изображения. В Glide это включено по умолчанию, но вы можете отключить его, вызвав dontAnimate() , что приведет к отображению изображения без анимации.

Вы также можете настроить анимацию кроссфейда, вызывая crossFade(int duration) , передавая длительность в миллисекундах, чтобы ускорить или замедлить ее — по умолчанию 300 миллисекунд.

Очень просто отобразить анимированный GIF в вашем приложении с помощью Glide. Это работает так же, как отображение обычного изображения.

1
2
3
4
5
6
7
8
ImageView gifImageView = (ImageView) findViewById(R.id.iv_gif);
 
Glide.with(this)
        .load(«http://i.imgur.com/Vth6CBz.gif»)
        .asGif()
        .placeholder(R.drawable.ic_cloud_off_red)
        .error(R.drawable.ic_cloud_off_red)
        .into(gifImageView);

Если вы ожидаете, что изображение будет GIF, вызовите asGif() — это гарантирует, что Glide получит GIF, в противном случае загрузка не удастся, и вместо этого будет показано Drawable переданное методу .error() .

К сожалению, Glide не поддерживает загрузку и отображение видео через URL. Вместо этого он может только загружать и отображать видео, уже сохраненные на телефоне. Показать видео, передав его URI методу load() .

1
2
3
Glide.with(context)
     .load(Uri.fromFile(new File(«your/video/file/path»))
     .into(imageView)

Прекрасная работа! В этом учебном пособии вы создали полное приложение для создания галереи изображений с помощью Glide, а также узнали, как работает библиотека и как ее интегрировать в свой собственный проект. Вы также узнали, как отображать как локальные, так и удаленные изображения, как отображать анимированные GIF-файлы и видео, и как применять преобразования изображений, такие как изменение размера. Не только это, но вы уже видели, как легко включить кэширование, обработку ошибок и прослушивание пользовательских запросов.

Чтобы узнать больше о Glide, вы можете обратиться к его официальной документации . Чтобы узнать больше о кодировании для Android, ознакомьтесь с другими нашими курсами и учебными пособиями здесь, на Envato Tuts +!

  • Android SDK
    Как звонить и использовать SMS в приложениях для Android
  • Android SDK
    Android Things: ваш первый проект
    Пол Требилкокс-Руис
  • Android SDK
    Создать Android Cardboard 360 Video Viewer
    Пол Требилкокс-Руис
  • Android SDK
    Делайте фотографии с помощью приложения для Android
    Ашраф Хатхибелагал