๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ’ป ๊ฐœ๋ฐœ์ž ์ด์•ผ๊ธฐ/์•ˆ๋“œ๋กœ์ด๋“œ ์‚ฝ์งˆ๊ธฐ

์‰ฝ๊ณ  ๋น ๋ฅด๊ฒŒ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•, Glide Library

by ์ •์„ ํ•œ 2022. 10. 18.
728x90
๋ฐ˜์‘ํ˜•

Glide Logo Image

์˜ค๋Š˜์€ ์•ˆ๋“œ๋กœ์ด๋“œ์—์„œ ์ด๋ฏธ์ง€๋ฅผ ๋กœ๋“œํ•˜๊ธฐ ์œ„ํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” Glide ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€ํ•ด์„œ ์ •๋ฆฌ๋ฅผ ํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ €๋Š” ํ˜„์—…์—์„œ ๊ฐœ๋ฐœ์„ ํ•  ๋•Œ์—๋„ ์ด๋ฏธ์ง€์™€ ๊ด€๋ จํ•œ ์ฒ˜๋ฆฌ๋Š” Glide๋ฅผ ํ†ตํ•ด์„œ ๊ฐœ๋ฐœ์„ ํ•ด์™”์—ˆ์Šต๋‹ˆ๋‹ค.

์™œ Glide๋ฅผ ์‚ฌ์šฉํ–ˆ๋А๋ƒ๊ณ  ํ•œ๋‹ค๋ฉด “… ๋นจ๋ผ์„œ…”๋ผ๊ณ ๋ฐ–์— ๋‹ต์„ ํ•  ์ˆ˜ ์—†์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ณดํ†ต ์ด๋ฏธ์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ๋•Œ์—๋Š” app์ž์ฒด์˜ drawable ์ด๋ฏธ์ง€๋ฅผ ๋กœ๋“œํ•ด์˜ค๊ธฐ๋„ ํ•˜์ง€๋งŒ ์„œ๋ฒ„ ํ˜น์€ ์™ธ๋ถ€ ์ด๋ฏธ์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋Œ€๋‹ค์ˆ˜์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋น ๋ฅด๊ฒŒ ์ด๋ฏธ์ง€ ๋กœ๋“œ ๊ธฐ๋Šฅ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ํŽธ๋ฆฌํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ์™„์„ฑํ•˜๋Š” ๊ฒƒ์˜ ์ฃผ๋œ ๋ชฉ์ ์ž…๋‹ˆ๋‹ค. ์ด๋•Œ Glide์—์„œ ์ง€์›ํ•˜๋Š” ๋‹ค์–‘ํ•œ ๋ฉ”์†Œ๋“œ ๋ฐ ํด๋ž˜์Šค๋“ค์„ ์ด์šฉํ•˜๋ฉด ์ •๋ง ์‰ฝ๊ฒŒ ๋‹ค์–‘ํ•œ ์ด๋ฏธ์ง€ ๋กœ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Glide ์ ์šฉ ๋ฐฉ๋ฒ•

Module ์˜ build.gradle์— ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ข…์†์„ฑ ์ถ”๊ฐ€

  • ํ˜„์žฌ bumptech์˜ ๋ฐฐํฌ ๋ฒ„์ „์€ 4.14.2 ๋ฒ„์ „์ด ์ตœ์‹  ๋ฒ„์ „์ž…๋‹ˆ๋‹ค.
  • Glide Github Link : https://github.com/bumptech/glide
// Glide
    implementation 'com.github.bumptech.glide:glide:4.12.0'
    implementation 'com.github.bumptech.glide:annotations:4.12.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
    kapt 'com.github.bumptech.glide:compiler:4.12.0'

ImageView์— Glide ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ด Url๋กœ Image๋ฅผ ์ ์šฉ

Glide.with(albumThumbnail)
      .load(items.contentUri)
      .into(albumThumbnail)

id=”@+id/albumThumbnail”์„ ๊ฐ€์ง„ ImageView์— items.contentUri์˜ ์ด๋ฏธ์ง€๋ฅผ ๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ •๋ง ๊ฐ„๋‹จํ•˜์ง€ ์•Š๋‚˜์š”? ์–ด๋–ค ๊ณณ์— ์–ด๋–ค ์ด๋ฏธ์ง€๋ฅผ ๋“ฑ๋กํ•œ๋‹ค! ์ด๊ฒŒ ์‚ฌ์‹ค ๋์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ๋งŒ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๊ตณ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์—†๊ฒ ์ฃ . ๋” ๋งŽ์€ ๊ธฐ๋Šฅ์„ ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์•ผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์˜๋ฏธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

Glide.with()
    .load()
    .transform() 
    /*
        .centerCrop() 
        .centerInside() 
        .circleCrop() 
        .fitCenter() 
        .rotate() 
        .roundedCorners()
    */
    .placeholder()
    .error()
    .fallback()
    .thumbnail()
    .into()

์œ„์˜ ๋ฉ”์†Œ๋“œ๋“ค๋กœ ๊ธฐ๋ณธ์ ์ธ ์ด๋ฏธ์ง€์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋Š” ๋Œ€๋ถ€๋ถ„ ํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • with() : Activity, Fragment์˜ context๋ฅผ ๋“ฑ๋ก
  • load() : ์ด๋ฏธ์ง€ ๋กœ๋“œ (๋‹ค์–‘ํ•œ ๋ฐฉ์‹์˜ ์ด๋ฏธ์ง€๋ฅผ ๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Œ)
  • into() : ์ด๋ฏธ์ง€๋ฅผ ๋กœ๋“œํ•  View๋ฅผ ๋“ฑ๋ก
  • transform() : ์ด๋ฏธ์ง€๋ฅผ ๋ณ€ํ˜•ํ•˜์—ฌ View์— ํ‘œ์‹œ (๋‹ค์–‘ํ•œ ๋ณ€ํ˜• ๊ด€๋ จ ๋ฉ”์†Œ๋“œ ์‚ฌ์šฉ)
  • placeholder() : ์ด๋ฏธ์ง€๋ฅผ ๋กœ๋“œํ•˜๊ธฐ ์ „์— ๋ณด์—ฌ์ค„ ์ž„์‹œ ์ด๋ฏธ์ง€ ๋“ฑ๋ก
  • error() : ์ด๋ฏธ์ง€๋ฅผ ๋กœ๋“œํ•˜๋Š” ๊ณผ์ •์—์„œ error๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ, ๋ณด์—ฌ์ค„ ์ด๋ฏธ์ง€ ๋“ฑ๋ก
  • fallback() : ๋กœ๋“œํ•  ์ด๋ฏธ์ง€๊ฐ€ null์ผ๋•Œ, ๋ณด์—ฌ์ค„ ์ด๋ฏธ์ง€ ๋“ฑ๋ก
  • thumbnail() : ์ด๋ฏธ์ง€์˜ thumbnail์„ ์ƒ์„ฑ (ํ•ด์ƒ๋„๊ฐ€ ํฐ ์ด๋ฏธ์ง€์˜ ๊ฒฝ์šฐ ๋กœ๋“œ ์†๋„ ๊ฐœ์„ ์„ ์œ„ํ•ด thumbnail๋กœ ์ผ์ • ์ด๋ฏธ์ง€์˜ ํ•ด์ƒ๋„๋ฅผ ๋‚ฎ์ถ”์–ด ๋…ธ์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ.)

์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด์„œ ์ถ”๊ฐ€์ ์œผ๋กœ ์„ค๋ช…์„ ๋”ํ•ด๋ณด์ž๋ฉด ์œ„ ์ด๋ฏธ์ง€๋ฅผ ์ž์„ธํžˆ ๋ณด์‹œ๋ฉด ์œ„์ชฝ์— radius์ฒ˜๋ฆฌ๊ฐ€ ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ํ•ด๋‹น ๋‚ด์šฉ์„ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•˜์˜€๋Š”์ง€ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด์„œ ํ•จ๊ป˜ ์„ค๋ช…ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

  • ๋””๋ฐ”์ด์Šค์˜ ์ด๋ฏธ์ง€, ๋™์˜์ƒ์„ ์ฝ์–ด์™€ ์ €์žฅ ํด๋”๋ณ„ ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ตฌํ˜„.
  • ํด๋” ๋ช…, ํด๋” ๋ณ„ ์ด๋ฏธ์ง€ ๊ฐœ์ˆ˜, ํด๋”์˜ ์ฒซ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์ธ๋„ค์ผ์„ ์•„์ดํ…œ์œผ๋กœ ๊ตฌ์„ฑํ•˜์—ฌ ๋…ธ์ถœ.

์ถ”๊ฐ€์ ์œผ๋กœ ์™ธ๋ถ€ ์„œ๋ฒ„๋ฅผ ํ†ตํ•œ ์ด๋ฏธ์ง€ ๋กœ๋“œ ๊ณผ์ •์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋กœ๋“œ ์‹œ๊ฐ„ ๋ฐ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๋Š” ๋”ฐ๋กœ ํ•ด์ฃผ์ง€ ์•Š๊ณ  ๋กœ๋“œํ•˜๋Š” ๊ณผ์ •๋งŒ ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์ด๋ฏธ์ง€ ๋ผ์šด๋”ฉ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ์˜ˆ์ œ์ฝ”๋“œ

fragment_main.xml ์—์„œ RecyclerView๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด include ํ•˜์—ฌ ํ•ด๋‹น xml์„ ๋กœ๋“œํ•˜์˜€์Šต๋‹ˆ๋‹ค.

<include
    android:id="@+id/folder_content"
    layout="@layout/folder_content_scrolling"/>

folder_content_scrolling.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:visibility="visible" />

MainFragment ์ฝ”๋“œ์—์„œ RecyclerView์— ๋Œ€ํ•œ ์„ค์ •์„ ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

val gridLayoutManager = GridLayoutManager(requireContext(), 2)

binding.folderContent.recyclerView.apply {
    layoutManager = gridLayoutManager
    adapter = mainAdapter
}
  • GridLayoutManager๋ฅผ ์ด์šฉํ•˜์—ฌ 2์—ด์„ ๊ฐ€์ง€๋Š” RecylerView๋ฅผ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค. mvvm, databinding์˜ ์ ์šฉ์œผ๋กœ folderContent(RecyclerView)์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ์„ค์ •์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

MainFragment์—์„œ mainAdapter์— ๊ด€ํ•œ ์ด๋ฒคํŠธ๋“ค์„ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•˜๋Š” ์ฝ”๋“œ๋„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

private val mainAdapter by lazy {
    MainAdapter { itemClick ->
        val action = MainFragmentDirections.actionMainFragmentToDetailFragment()
        findNavController().navigate(action)
        viewModel.setSelectedFolder(itemClick)
    }
}
  • MainAdapter๋ฅผ mainAdapter์— ๊ตฌ์„ฑํ•˜๊ณ  itemClick ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ†ตํ•ด RecyclerView์˜ ํด๋ฆญ ์ด๋ฒคํŠธ๋ฅผ ์—ฐ๊ฒฐํ•˜์—ฌ ํ•ด๋‹น ์•„์ดํ…œ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ ์–ด๋–ค ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ• ์ง€๋ฅผ MainFragment๋‚ด๋ถ€์— ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
  • ์ €๋Š” Navigation์ฝ”๋“œ๋ฅผ ์—ฐ๊ฒฐํ•˜์—ฌ ๋‹ค์Œ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ด๋ฒคํŠธ ์ฝœ๋ฐฑ์„ ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

MainAdapter์„ ๊ตฌ์„ฑํ•˜์—ฌ RecyclerView์˜ ์•„์ดํ…œ๋“ค์„ ์—ฐ๊ฒฐํ•ด์ค๋‹ˆ๋‹ค.

package com.seonhan_dev.imagepicker.ui.main

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners
import com.seonhan_dev.imagepicker.data.model.MediaStoreFolder
import com.seonhan_dev.imagepicker.databinding.ItemAlbumThumbBinding

class MainAdapter(private var itemClick: (MediaStoreFolder) -> Unit) :
    ListAdapter<MediaStoreFolder, MainAdapter.ViewHolder> (MediaStoreFolder.DiffCallback) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding = ItemAlbumThumbBinding.inflate(
            LayoutInflater.from(parent.context),
            parent,
            false
        )

        return ViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        getItem(position)?.let {
            holder.bind(it, holder)
        }
    }

    inner class ViewHolder(private val binding: ItemAlbumThumbBinding) :
        RecyclerView.ViewHolder(binding.root) {

        fun bind(items: MediaStoreFolder, holder: ViewHolder) {
            with(binding) {
                albumName.text = items.item
                albumCount.text = items.count.toString() + " Images"

                Glide.with(albumThumbnail)
                    .load(items.contentUri)
                    .transform(
                        CenterCrop(),
                        GranularRoundedCorners(27f, 27f, 0f, 0f)
                    )
                    .into(albumThumbnail)
            }

            itemView.setOnClickListener {
                itemClick(items)
            }
        }
    }

}

item_album_thumb.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/mainFragment_content_margin"
        android:elevation="@dimen/mainFragment_content_elevation">

        <ImageView
            android:id="@+id/album_thumbnail"
            android:layout_width="match_parent"
            android:layout_height="@dimen/mainFragment_content_image_height"
            android:scaleType="centerCrop"/>

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="@dimen/mainFragment_content_title_height"
            android:background="@drawable/bottom_corner_radius">

            <TextView
                android:id="@+id/album_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="9dp"
                android:layout_marginStart="8dp"
                android:textSize="@dimen/content_title_text_size"
                android:textColor="@color/colorTextDark"
                android:textStyle="bold"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/album_count"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="@dimen/content_count_margin_bottom"
                android:layout_marginStart="8dp"
                android:textSize="@dimen/content_count_text_size"
                android:textColor="@color/colorTextDark"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/album_name"/>

        </androidx.constraintlayout.widget.ConstraintLayout>


    </LinearLayout>


</layout>
  • RecyclerView์˜ ๊ตฌํ˜„์— ๊ด€ํ•˜์—ฌ ์ž์„ธํžˆ ๋‹ค๋ฃจ์ง€๋Š” ์•Š๊ณ  Glide ๊ตฌํ˜„๋ถ€๋ฅผ ๋” ์ž์„ธํžˆ ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
  • ๋””๋ฐ”์ด์Šค์˜ ์ด๋ฏธ์ง€ ์ •๋ณด๋Š” MediaStoreFolder ๋ชจ๋ธ์— ์ €์žฅ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ๋ชจ๋ธ์„ items๋กœ ๋ฐ›์•„ ํ™”๋ฉด์— ๋…ธ์ถœํ•ด์•ผ ํ•  ์ •๋ณด๋“ค์„ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.
  • ์ €๋Š” ์ด๋•Œ ์ด๋ฏธ์ง€์˜ ์œ—๋ถ€๋ถ„์— radius์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์–ด์•ผ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— transform() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ CenterCrop(), GranularRoundedCorners(27f, 27f, 0f, 0f)์˜ ๋‚ด์šฉ์„ ๋“ฑ๋กํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • GranularRoundedCorners ๋ฉ”์†Œ๋“œ๋Š” Glide์˜ public final class GranularRoundedCorners extends BitmapTransformation๋กœ ๊ตฌํ˜„๋˜์–ด์žˆ๋Š” ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.
  private final float topLeft;
  private final float topRight;
  private final float bottomRight;
  private final float bottomLeft;

  /** Provide the radii to round the corners of the bitmap. */
  public GranularRoundedCorners(
      float topLeft, float topRight, float bottomRight, float bottomLeft) {
    this.topLeft = topLeft;
    this.topRight = topRight;
    this.bottomRight = bottomRight;
    this.bottomLeft = bottomLeft;
  }

์ด๋Ÿฐ ๊ณผ์ •๋“ค์„ ํ†ตํ•˜์—ฌ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ•ด๋‹น ์˜ˆ์‹œ ์ด๋ฏธ์ง€์™€ ๊ฐ™์€ ๋‚ด์šฉ์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์œ„์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ์‰ฝ๊ณ  ๊ฐ„๋‹จํ•˜๊ฒŒ Glide ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ด ํ•„์š”ํ•œ ๊ณณ์— ์ ์šฉํ•ด๋ณด์‹œ๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

728x90
๋ฐ˜์‘ํ˜•