개발 공부/안드로이드

[Compose] 컴포즈 공부하기4 - Image (with painter)

yong_DD 2023. 8. 30. 17:25

컴포즈에서 이미지를 넣는 방법에는 여러 가지가 있다.

painter, imageVector, imageBitmap 그리고 인터넷에서 로드할 땐 AsyncImage(coil), GlideImage(glide)

 

Ⅰ. Image

Image는 compose 의 Image.kt를 들어가보면 어떤 값을 쓸 수 있는지도 자세히 알 수 있다.

(설명은 제외했다)

fun Image(
    bitmap: ImageBitmap,
    contentDescription: String?,
    modifier: Modifier = Modifier,
    alignment: Alignment = Alignment.Center,
    contentScale: ContentScale = ContentScale.Fit,
    alpha: Float = DefaultAlpha,
    colorFilter: ColorFilter? = null
) {
    Image(
        bitmap,
        contentDescription,
        modifier,
        alignment,
        contentScale,
        alpha,
        colorFilter,
        FilterQuality.Low
    )
}

@Composable
fun Image(
    bitmap: ImageBitmap,
    contentDescription: String?,
    modifier: Modifier = Modifier,
    alignment: Alignment = Alignment.Center,
    contentScale: ContentScale = ContentScale.Fit,
    alpha: Float = DefaultAlpha,
    colorFilter: ColorFilter? = null,
    filterQuality: FilterQuality = DefaultFilterQuality // 위의 메서드에서 이 부분 추가됨
) {
    val bitmapPainter = remember(bitmap) { BitmapPainter(bitmap, filterQuality = filterQuality) }
    Image(
        painter = bitmapPainter,
        contentDescription = contentDescription,
        modifier = modifier,
        alignment = alignment,
        contentScale = contentScale,
        alpha = alpha,
        colorFilter = colorFilter
    )
}

@Composable
fun Image(
    imageVector: ImageVector,
    contentDescription: String?,
    modifier: Modifier = Modifier,
    alignment: Alignment = Alignment.Center,
    contentScale: ContentScale = ContentScale.Fit,
    alpha: Float = DefaultAlpha,
    colorFilter: ColorFilter? = null
) = Image(
    painter = rememberVectorPainter(imageVector),
    contentDescription = contentDescription,
    modifier = modifier,
    alignment = alignment,
    contentScale = contentScale,
    alpha = alpha,
    colorFilter = colorFilter
)

@Composable
fun Image(
    painter: Painter,
    contentDescription: String?,
    modifier: Modifier = Modifier,
    alignment: Alignment = Alignment.Center,
    contentScale: ContentScale = ContentScale.Fit,
    alpha: Float = DefaultAlpha,
    colorFilter: ColorFilter? = null
) {
    val semantics = if (contentDescription != null) {
        Modifier.semantics {
            this.contentDescription = contentDescription
            this.role = Role.Image
        }
    } else {
        Modifier
    }

    // Explicitly use a simple Layout implementation here as Spacer squashes any non fixed
    // constraint with zero
    Layout(
        {},
        modifier.then(semantics).clipToBounds().paint(
            painter,
            alignment = alignment,
            contentScale = contentScale,
            alpha = alpha,
            colorFilter = colorFilter
        )
    ) { _, constraints ->
        layout(constraints.minWidth, constraints.minHeight) {}
    }
}

위의 코드를 보면 결국엔 다 painter로 변경하여 이미지를 그리고 있다.

이미지를 넣을때 , painter, imageVector, imageBitamp으로 넣을 수 있지만 사용자의 편리성을 위한 부분으로 보인다.


그렇다면 이 Painter는 무엇일까?

안드로이드 디벨로포 맞춤 페인터 부분에서는 이렇게 설명이 나온다.

Compose에서 Painter 객체는 그릴 수 있는 항목을 나타내고(Android에서 정의된 Drawable API의 대체 API) 객체를 사용하고 있는 컴포저블의 측정 및 레이아웃에 영향을 미치는 데 사용됩니다.


 Painter.kt(abstract class)를 들어가보면 이렇게 설명이 나온다.

Abstraction for something that can be drawn. In addition to providing the ability to draw into a specified bounded area, Painter provides a few high level mechanisms that consumers can use to configure how the content is drawn. These include alpha, ColorFilter, and RTL

대략 요약하자면 그려질 수 있는 것에 대한 추상화이며 지정된 영역에 그릴 수 있는 기능을 제공하고 그릴 때 사용할 수 있는 alpha, colorFilter, RTL(LayoutDirection) 기능을 제공해준 다는 것이다.

(참고 : Painter는 androidx.compose.ui.painter package에 있다.)

 

즉 painter는 단어 그대로 그릴 수 있는 것이며 alpha, colorfilter, RTL 기능을 제공해주는 것이라고 볼 수 있다!

 

 

같은 painter로 받는 것이지만 사용할 때는 따른 형식으로 넣기 때문에 넣는 형식에 따라 예시로 알아보자


1. painter

1-1 painterResource API를 사용해 drawable에 있는 것을 가져다 보여줄 때 사용한다.

    Image(
             painter = painterResource(id = R.drawable.apple),
             contentDescription = "사과"
         )

1-2 custom painter

class OverlayImagePainter constructor(
    private val image: ImageBitmap,
    private val imageOverlay: ImageBitmap,
    private val srcOffset: IntOffset = IntOffset.Zero,
    private val srcSize: IntSize = IntSize(image.width, image.height),
    private val overlaySize: IntSize = IntSize(imageOverlay.width, imageOverlay.height)
) : Painter() {
	....
}

val rainbowImage = ImageBitmap.imageResource(id = R.drawable.rainbow)
val dogImage = ImageBitmap.imageResource(id = R.drawable.dog)
val customPainter = remember {
    OverlayImagePainter(dogImage, rainbowImage)
}
Box(
    modifier =
    Modifier.background(color = Color.Gray)
        .padding(30.dp)
        .background(color = Color.Yellow)
        .paint(customPainter)
) { /** intentionally empty **/ }

Painter를 상속받아 직접 그리도록 하는 방법 (상세한 설명 및 코드는 여기를 참고)

 

2. ImageBitmap

Compose에서 래스터 이미지(Bitmap이라고도 함)는 ImageBitmap 인스턴스에 로드할 수 있으며 
BitmapPainter는 화면에 비트맵을 그리는 역할을 합니다.

2-1 asImageBitmap()

Image(
    bitmap = bitmap.asImageBitmap(),
    contentDescription = "사과"
)

Bitmap을 넣어야할 때 asImageBitamp을 사용하여 ImageBitmap으로 변경할 수 있다.

 

2-2 ImageBitmap.imageResource

val imageBitmap = ImageBitmap.imageResource(R.drawable.dog)

ImageBitmap 자체에 액세스 해야하는 경우 위와 같이 하면 된다.

 

 

3. ImageVector

VectorPainter는 ImageVector를 화면에 그리는 역할을 합니다. 
ImageVector는 SVG 명령어의 하위 집합을 지원합니다.

1-1 Icons 사용

Image(
    imageVector = Icons.Filled.Settings,
    contentDescription = "설정"
)

* Icons : material design system으로 직접 제공하는 아이콘, Filled / Outlined / Rounded / TwoTone / Sharp가 있음.

https://developer.android.com/images/reference/androidx/compose/material/icons/iconography.png

 

2-1 ImageVector.vectorResource

val imageVector = ImageVector.vectorResource(id = R.drawable.baseline_shopping_cart_24)

ImageVector 자체에 액세스 해야하는 경우 위와 같이 하면 된다.

 


Ⅱ. 인터넷 이미지 로드

coil과 glide 그리고 fresco를 사용할 수 있는데 fresco는 파이프라인을 셋팅해야하기도 하고, 간편하게 많이들 사용하는  coil과 glide에 대해서만 설명하겠다!

 

1. Coil - AsyncImage

implementation("io.coil-kt:coil-compose:2.4.0")

gradle에 coil-compose를 추가해주고 사용해주면 된다. 

AsyncImage(
    model = "https://example.com/image.jpg",
    contentDescription = "Translated description of what the image contains"
)

 

2. Glide - GlideImage

implementation "com.github.bumptech.glide:compose:1.0.0-alpha.5"

gradle에 glide:compose를 추가해주고 사용해주면 된다.

GlideImage(
  model = myUrl,
  contentDescription = getString(R.id.picture_of_cat),
  modifier = Modifier.padding(padding).clickable(onClick = onClick).fillParentMaxSize(),
)

 

 

 

++ 추가로 기존의 xml에서 사용하던 scaleType은 ContentScale을 사용하고,

compose에서는 이미지를 동그랗게 자른다든지 라운드를 주는 것도 굉장히 간편하게 되어있다!!!

자세한 부분은 여기에 잘나와있으니 참고해보면 좋을 것 같다!

 

 


참고 및 출처

 

맞춤 페인터  |  Jetpack Compose  |  Android Developers

맞춤 페인터 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Compose에서 Painter 객체는 그릴 수 있는 항목을 나타내고(Android에서 정의된 Drawable API의 대체 API)

developer.android.com

 

이미지 맞춤설정  |  Jetpack Compose  |  Android Developers

이미지 맞춤설정 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 이미지는 Image 컴포저블의 속성(contentScale, colorFilter)을 사용하여 맞춤설정할 수 있습니다.

developer.android.com

 

이미지 로드  |  Jetpack Compose  |  Android Developers

이미지 로드 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 디스크에서 이미지 로드 Image 컴포저블을 사용하여 화면에 그래픽을 표시합니다. 디스크에서 이

developer.android.com

 

ImageBitmap과 ImageVector 비교  |  Jetpack Compose  |  Android Developers

ImageBitmap과 ImageVector 비교 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 가장 일반적인 이미지 형식 두 가지는 래스터 이미지와 벡터 이미지입니다. 래스터

developer.android.com