Статьи

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

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

Picasso — это популярная библиотека Android с открытым исходным кодом для загрузки как локальных, так и удаленных изображений. Узнайте, как легко использовать его для обработки ваших потребностей загрузки изображений.

Picasso (имя, вдохновленное известным французским художником Пабло Пикассо) — очень популярная библиотека Android с открытым исходным кодом для загрузки изображений в ваше приложение Android. Согласно официальным документам, в нем говорится:

… Picasso позволяет без проблем загружать изображения в ваше приложение — часто в одну строку кода!

Обратите внимание, что Picasso использует OkHttp (сетевую библиотеку того же разработчика) для загрузки изображений через Интернет.

Теперь вы узнали, что такое Пикассо. Следующий вопрос, который вы можете задать, — зачем его использовать?

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

Вот несколько распространенных ошибок при загрузке изображений на Android, о которых вам говорит Пикассо, согласно официальным документам:

  • обработка утилизации ImageView и отмена загрузки в адаптере
  • сложные преобразования изображения с минимальным использованием памяти
  • автоматическое кеширование памяти и диска

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

Пример проекта (на Kotlin) для этого учебного руководства можно найти в нашем репозитории GitHub, чтобы вы могли легко следить за ним.

Хорошие художники копируют, великие художники крадут. — Пабло Пикассо

Чтобы следовать этому руководству, вам понадобится:

PicassoDemo Android Studio и создайте новый проект (вы можете назвать его PicassoDemo ) с пустым действием MainActivity . Не забудьте также установить флажок Включить поддержку Kotlin .

Android Studios Диалог добавления активности на мобильный

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

1
2
3
4
dependencies {
    implementation ‘com.android.support:recyclerview-v7:27.1.1’
    implementation ‘com.squareup.picasso:picasso:2.71828’
}

Или с Maven:

1
2
3
4
5
<dependency>
  <groupId>com.squareup.picasso</groupId>
  <artifactId>picasso</artifactId>
  <version>2.71828</version>
</dependency>

Обязательно синхронизируйте проект после добавления Picasso и артефактов RecyclerView v7.

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

Так иди сделай это сейчас!

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

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

Мы начнем с создания нашего RecyclerView внутри файла макета activity_main.xml .

01
02
03
04
05
06
07
08
09
10
11
12
<?xml version=»1.0″ encoding=»utf-8″?>
<RelativeLayout
        xmlns:android=»http://schemas.android.com/apk/res/android»
        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 ( item_image.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 для высокопроизводительной передачи данных из одного компонента в другой в Android. В нашем случае данные будут транспортироваться из SunsetGalleryActivity в SunsetPhotoActivity .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
data class SunsetPhoto(val url: String) : Parcelable {
 
    constructor(parcel: Parcel) : this(parcel.readString())
 
    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(url)
    }
 
    override fun describeContents(): Int {
        return 0
    }
 
    companion object CREATOR : Parcelable.Creator<SunsetPhoto> {
        override fun createFromParcel(parcel: Parcel): SunsetPhoto {
            return SunsetPhoto(parcel)
        }
 
        override fun newArray(size: Int): Array<SunsetPhoto?> {
            return arrayOfNulls(size)
        }
    }
}

Обратите внимание, что эта модель SunsetPhoto имеет только одно поле с именем url (для демонстрационных целей), но вы можете иметь больше, если хотите. Этот класс реализует Parcelable , что означает, что мы должны переопределить некоторые методы.

Мы можем использовать Android Studio IDEA для создания этих методов для нас, но недостатком этого является обслуживание. Как? Каждый раз, когда мы добавляем новые поля в этот класс, мы можем забыть явно обновить методы constructor и writeToParcel , что может привести к некоторым ошибкам, если мы не обновим методы.

Теперь, чтобы обойти обновление или написание этих стандартных методов, Kotlin 1.1.14 представил аннотацию @Parcelize . Эта аннотация поможет нам writeToParcel сгенерировать writeToParcel , writeFromParcel и writeFromParcel .

1
2
@Parcelize
data class SunsetPhoto(val url: String) : Parcelable

Теперь наш код класса SunsetPhoto состоит из двух строк! Потрясающие!

Не забудьте добавить следующий код в модуль приложения build.gradle :

1
2
3
androidExtensions {
    experimental = true
}

Кроме того, я включил сопутствующий объект (или статический метод в Java) getSunsetPhotos() в SunsetPhoto модели SunsetPhoto который будет просто возвращать ArrayList объекта SunsetPhoto при вызове.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
@Parcelize
data class SunsetPhoto(val url: String) : Parcelable {
 
    companion object {
        fun getSunsetPhotos(): Array<SunsetPhoto> {
            return arrayOf<SunsetPhoto>(SunsetPhoto(«https://goo.gl/32YN2B»),
                    SunsetPhoto(«https://goo.gl/Wqz4Ev»),
                    SunsetPhoto(«https://goo.gl/U7XXdF»),
                    SunsetPhoto(«https://goo.gl/ghVPFq»),
                    SunsetPhoto(«https://goo.gl/qEaCWe»),
                    SunsetPhoto(«https://goo.gl/vutGmM»))
        }
    }
}

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

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
class MainActivity : AppCompatActivity() {
    //…
    private inner class ImageGalleryAdapter(val context: Context, val sunsetPhotos: Array<SunsetPhoto>)
     : RecyclerView.Adapter<ImageGalleryAdapter.MyViewHolder>() {
 
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageGalleryAdapter.MyViewHolder {
            val context = parent.context
            val inflater = LayoutInflater.from(context)
            val photoView = inflater.inflate(R.layout.item_image, parent, false)
            return MyViewHolder(photoView)
        }
 
        override fun onBindViewHolder(holder: ImageGalleryAdapter.MyViewHolder, position: Int) {
            val sunsetPhoto = sunsetPhotos[position]
            val imageView = holder.photoImageView
        }
 
        override fun getItemCount(): Int {
            return sunsetPhotos.size
        }
 
        inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
 
            var photoImageView: ImageView = itemView.findViewById(R.id.iv_photo)
 
            init {
                itemView.setOnClickListener(this)
            }
 
            override fun onClick(view: View) {
                val position = adapterPosition
                if (position != RecyclerView.NO_POSITION) {
                    val sunsetPhoto = sunsetPhotos[position]
                    val intent = Intent(context, SunsetPhotoActivity::class.java).apply {
                        putExtra(SunsetPhotoActivity.EXTRA_SUNSET_PHOTO, sunsetPhoto)
                    }
                    startActivity(intent)
                }
            }
        }
    }
}

Обратите внимание, что мы использовали функцию apply расширения, чтобы поместить объект как дополнительный объект в намерение. В качестве напоминания функция apply возвращает объект, переданный ей в качестве аргумента (т.е. объект-получатель).

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

01
02
03
04
05
06
07
08
09
10
11
12
override fun onBindViewHolder(holder: ImageGalleryAdapter.MyViewHolder, position: Int) {
    val sunsetPhoto = sunsetPhotos[position]
    val imageView = holder.photoImageView
 
    Picasso.get()
            .load(sunsetPhoto.url)
            .placeholder(R.drawable.placeholder)
            .error(R.drawable.error)
            .fit()
            .into(imageView)
 
}

Шаг за шагом, вот что делают звонки Picasso :

Это возвращает глобальный экземпляр Picasso (экземпляр singleton), инициализированный со следующими конфигурациями по умолчанию:

  • Кэш-память LRU составляет 15% доступной оперативной памяти приложения.
  • Дисковый кеш объемом 2%, до 50 МБ, но не менее 5 МБ. Примечание: это доступно только для API 14+.
  • Три темы загрузки для доступа к диску и сети.

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

01
02
03
04
05
06
07
08
09
10
11
val picassoBuilder = Picasso.Builder(context)
// do custom configurations
 
// Specify the {@link Downloader} that will be used for downloading images.
picassoBuilder.downloader()
// Specify the ExecutorService for loading images in the background.
picassoBuilder.executor()
// Specify the memory Cache used for the most recent images.
picassoBuilder.memoryCache()
// and more
val picasso = picassoBuilder.build()

Наконец, вы вызываете метод build() чтобы вернуть вам экземпляр Picasso с вашими собственными конфигурациями.

Рекомендуется сделать это в вашем Application.onCreate а затем установить его в качестве одноэлементного экземпляра с Picasso.setSingletonInstance в этом методе, чтобы убедиться, что экземпляр Picasso является глобальным.

load(String path) запускает запрос изображения, используя указанный путь. Этот путь может быть удаленным URL, файловым ресурсом, ресурсом контента или ресурсом Android.

  • placeholder(int placeholderResId) : локальный идентификатор ресурса-заполнителя или нарисованный для использования во время загрузки и последующего отображения изображения. Он служит хорошим пользовательским интерфейсом для отображения изображения заполнителя во время загрузки изображения.

Обратите внимание, что Picasso сначала проверяет, находится ли запрошенное изображение в кеше памяти, и если оно есть, оно отображает изображение оттуда (мы обсудим кеширование в Picasso подробнее в следующем разделе).

  • error(int errorResId) : отрисовка для использования, если запрошенное изображение не может быть загружено — возможно, из-за того, что веб-сайт не работает.
  • noFade() : Пикассо всегда исчезает в изображении, которое отображается в ImageView . Если вам не нужна эта постепенная анимация, просто вызовите метод noFade() .
  • into(ImageView imageView) : представление целевого изображения, в которое будет помещено изображение.

Если сервер, с которого вы запрашиваете изображение, не дает нужного вам изображения в требуемом размере, вы можете легко изменить его размер, используя resize(int targetWidth, int targetHeight) . Вызов этого метода изменяет размер изображения и затем отображает его в ImageView . Обратите внимание, что размеры указаны в пикселях (px), а не в dp.

1
2
3
4
5
Picasso.get()
       .load(sunsetPhoto.url)
       .placeholder(R.drawable.placeholder)
       .resize(400, 200)
       .into(imageView)

Вы можете передать ресурс измерения Android для ширины и высоты с помощью метода resizeDimen(int targetWidthResId, int targetHeightResId) . Этот метод преобразует размерный размер в необработанные пиксели, а затем вызывает resize() под капотом, передавая преобразованные размеры (в пикселях) в качестве аргументов.

1
2
3
4
Picasso.get()
  //…
   .resizeDimen(R.dimen.list_detail_image_size, R.dimen.list_detail_image_size)
   //…

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

К счастью, Picasso дает нам несколько полезных методов для решения этой проблемы:

  • centerCrop() : равномерно масштабирует изображение (поддерживая соотношение сторон изображения), так что изображение заполняет заданную область, с максимально возможным отображением изображения. При необходимости изображение будет обрезано по горизонтали или вертикали для соответствия. Вызов этого метода обрезает изображение внутри границ, указанных в resize() .
  • centerInside() : масштабирует изображение так, чтобы оба измерения были равны или меньше запрошенных границ. Это будет центрировать изображение внутри границ, указанных resize() .
  • onlyScaleDown() : изменять размер изображения только в том случае, если исходный размер изображения больше целевого размера, указанного в resize() .
  • fit() : попытаться изменить размер изображения, чтобы оно точно ImageView границы целевого ImageView .

Picasso имеет простой API-интерфейс для поворота изображения и его отображения. Метод rotate(float degrees) поворачивает изображение на указанные градусы.

1
2
3
4
Picasso.get()
   //…
   .rotate(90f)
   //…

В приведенном выше примере это повернет изображение на 90 градусов. Метод rotate(float degrees, float pivotX, float pivotY) поворачивает изображение на указанные градусы вокруг точки поворота.

1
2
3
4
Picasso.get()
   //…
   .rotate(30f, 200f, 100f)
   //…

Здесь мы собираемся повернуть изображение на 30 градусов вокруг точки поворота 200, 100 пикселей.

Помимо простого управления изображением путем его поворота, Picasso также дает нам возможность применить пользовательское преобразование к изображению перед его отображением.

Вы просто создаете класс, который реализует интерфейс Transformation Пикассо. Затем вам нужно переопределить два метода:

  • Bitmap transform(Bitmap source) : это преобразовывает исходное растровое изображение в новое растровое изображение.
  • String key() : возвращает уникальный ключ для преобразования, используемый для целей кэширования.

После того как вы завершили создание своего пользовательского преобразования, вы просто выполняете его, вызывая transform(Transformation transformation) в своем экземпляре Picasso. Обратите внимание, что вы также можете передать список Transformation для transform() .

1
2
3
4
Picasso.get()
       // …
       .transform(CropCircleTransformation())
       .into(imageView)

Здесь я применил преобразование кадрирования к изображению из библиотеки Android с открытым исходным кодом Picasso Transformations . В этой библиотеке есть много преобразований, которые вы можете применить к изображению с помощью Picasso, включая преобразования для размытия или серого изображения. Проверьте это, если хотите применить классные преобразования к вашим изображениям.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
class MainActivity : AppCompatActivity() {
 
    private lateinit var recyclerView: RecyclerView
    private lateinit var imageGalleryAdapter: ImageGalleryAdapter
 
    override fun onCreate(savedInstanceState: Bundle?) {
        //…
         
        val layoutManager = GridLayoutManager(this, 2)
        recyclerView = findViewById(R.id.rv_images)
        recyclerView.setHasFixedSize(true)
        recyclerView.layoutManager = layoutManager
        imageGalleryAdapter = ImageGalleryAdapter(this, SunsetPhoto.getSunsetPhotos())
    }
 
    override fun onStart() {
        super.onStart()
        recyclerView.adapter = imageGalleryAdapter
    }
    // …
}

Создайте новое действие и назовите его SunsetPhotoActivity . Мы получаем дополнительный SunsetPhoto и загружаем изображение — внутри onStart() — вместе с Пикассо, как мы делали раньше.

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
class SunsetPhotoActivity : AppCompatActivity() {
 
    companion object {
        const val EXTRA_SUNSET_PHOTO = «SunsetPhotoActivity.EXTRA_SUNSET_PHOTO»
    }
 
    private lateinit var imageView: ImageView
    private lateinit var sunsetPhoto: SunsetPhoto
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sunset_photo)
 
        sunsetPhoto = intent.getParcelableExtra(EXTRA_SUNSET_PHOTO)
        imageView = findViewById(R.id.image)
    }
 
    override fun onStart() {
        super.onStart()
 
        Picasso.get()
                .load(sunsetPhoto.url)
                .placeholder(R.drawable.placeholder)
                .error(R.drawable.error)
                .fit()
                .into(imageView)
    }
}

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

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:layout_width=»match_parent»
        android:layout_height=»match_parent»>
 
    <ImageView
            android:id=»@+id/image»
            android:layout_width=»match_parent»
            android:layout_height=»wrap_content»
            android:adjustViewBounds=»true»
            android:scaleType=»fitCenter»
            android:layout_gravity=»center»/>
</LinearLayout>

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

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

Вкратце, вот что происходит (под капотом) для запроса изображения: память -> диск -> сеть.

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

Вы можете избежать кэширования памяти, вызвав memoryPolicy(MemoryPolicy.NO_CACHE) . Это просто пропустит поиск в кэш-памяти при обработке запроса изображения.

1
2
3
4
5
6
7
Picasso.get()
       .load(sunsetPhoto.url)
       .placeholder(R.drawable.placeholder)
       .error(R.drawable.error)
       .fit()
       .memoryPolicy(MemoryPolicy.NO_CACHE)
       .into(imageView)

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

Но имейте в виду, что изображение все еще будет кэшироваться на диске — чтобы предотвратить это, вы также используете networkPolicy(@NonNull NetworkPolicy policy, @NonNull NetworkPolicy... additional) , которая принимает одно или несколько из следующих значений перечисления:

  • NetworkPolicy.NO_CACHE : пропускает проверку дискового кэша и вызывает загрузку через сеть.
  • NetworkPolicy.NO_STORE : пропускает сохранение результата в кэш диска.
  • NetworkPolicy.OFFLINE : форсирует запрос только через дисковый кеш, пропуская сеть.

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

1
2
3
4
5
6
7
8
Picasso.get()
       .load(sunsetPhoto.url)
       .placeholder(R.drawable.placeholder)
       .error(R.drawable.error)
       .fit()
       .memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
       .networkPolicy(NetworkPolicy.NO_CACHE)
       .into(imageView)

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

  • void onBitmapFailed(e: Exception?, errorDrawable: Drawable?) : срабатывает, когда изображение не может быть успешно загружено. Здесь мы можем получить доступ к исключению, которое было сгенерировано.
  • void onBitmapLoaded(Bitmap bitmap, LoadedFrom from) : срабатывает всякий раз, когда изображение было успешно загружено. Здесь мы получаем растровое изображение, чтобы показать пользователю.
  • void onPrepareLoad(Drawable placeHolderDrawable) : вызывается непосредственно перед отправкой вашего запроса.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
Picasso.get()
       .load(sunsetPhoto.url)
       .placeholder(R.drawable.placeholder)
       .error(R.drawable.error)
       .into(object : Target {
            
           override fun onPrepareLoad(placeHolderDrawable: Drawable?) {
           }
 
           override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {
           }
 
           override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {
           }
       })

Здесь вы также можете показать, а затем скрыть диалог прогресса, если он у вас есть.

Существует еще один приемник обратного вызова, который вы можете реализовать, который называется Callback . Этот интерфейс имеет только два метода: onSuccess() и onError(Exception e) . Первый вызывается, когда загрузка запроса изображения была успешной, а второй вызывается, когда при обработке запроса возникает ошибка.

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

Поэтому включите артефакт палитры в build.gradle модуля вашего приложения:

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

Теперь давайте реализуем интерфейс Callback в нашем запросе Пикассо.

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
override fun onStart() {
    super.onStart()
 
    Picasso.get()
            .load(sunsetPhoto.url)
            .placeholder(R.drawable.placeholder)
            .error(R.drawable.error)
            .fit()
            .into(imageView, object : Callback {
 
                override fun onSuccess() {
                    val bitmap = (imageView.drawable as BitmapDrawable).bitmap
                    onPalette(Palette.from(bitmap).generate())
                }
 
                override fun onError(e: Exception?) {
                }
            })
}
 
fun onPalette(palette: Palette?) {
    if (null != palette) {
        val parent = imageView.parent.parent as ViewGroup
        parent.setBackgroundColor(palette.getDarkVibrantColor(Color.GRAY))
    }
}

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

Конечный результат приложения

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

Вы просто вызываете priority() в своем экземпляре запроса Picasso и передаете любое из перечислений: Priority.LOW , Priority.NORMAL или Priority.HIGH .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Picasso.get()
        .load(sunsetPhoto.url)
        .placeholder(R.drawable.placeholder)
        .error(R.drawable.error)
        .fit()
        .priority(Picasso.Priority.HIGH)
        .into(imageView)
 
Picasso.get()
        .load(sunsetPhoto.url)
        .placeholder(R.drawable.placeholder)
        .error(R.drawable.error)
        .fit()
        .priority(Picasso.Priority.NORMAL)
        .into(imageView)
 
Picasso.get()
        .load(sunsetPhoto.url)
        .placeholder(R.drawable.placeholder)
        .error(R.drawable.error)
        .fit()
        .priority(Picasso.Priority.LOW)
        .into(imageView)

Помечая свои запросы Picasso, вы можете возобновлять, приостанавливать или отменять запросы, связанные с определенными тегами. В зависимости от вашего варианта использования вы можете пометить ваши запросы строкой или объектами, которые должны определять объем запроса как Context , действие или Fragment . Вы можете легко пометить запрос Пикассо, вызвав tag(@NonNull Object tag) для одного из них. Передайте ему экземпляр Object который служит тегом.

Вот следующие операции, которые вы можете выполнять с помеченными запросами Пикассо:

  • pauseTag(Object tag) : приостановить все запросы, связанные с данным тегом.
  • resumeTag(Object tag) : возобновить приостановленные запросы с данным тегом.
  • cancelTag(Object tag) : отменить любые существующие запросы с данным тегом.
1
2
3
Picasso.get()
       // …
       .tag(context

Хотя тегирование ваших запросов дает вам некоторый контроль над вашими запросами, вы должны быть очень осторожны при использовании тегов из-за возможной утечки памяти. Вот что говорится в официальной документации:

Picasso будет хранить ссылку на тег до тех пор, пока этот тег приостановлен и / или имеет активные запросы. Остерегайтесь потенциальных утечек.

Это просто для загрузки изображений локально в вашем приложении.

1
2
3
4
5
File file = new File(«your/pic/file/path/file.png»)
Picasso.get()
        .load(file)
        .fit()
        .into(imageView)

Хорошая работа! В этом руководстве вы создали полное приложение для создания галереи изображений с помощью Picasso, а также узнали, как работает библиотека и как ее интегрировать в собственный проект.

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

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