HOME > android > tips

안드로이드 Drawable을 Bitmap으로, Bitmap을 Drawable로 변경하는 방법

JSFollow28 Jul 2019

Android의 리소스들은 기본적으로 Drawable 객체로 가져옵니다. 하지만 간혹 코딩을 하다보면 Bitmap을 인자로 받는 애들이 있습니다. 예를 들어, 아래 코드에서 Notification을 생성할 때 setSmallIcon()는 인자로 Resource ID를 받습니다. 하지만 setLargeIcon()는 인자로 Bitmap을 받습니다.

val builder = NotificationCompat.Builder(this, channelId)
builder.setSmallIcon(R.drawable.ic_codechacha)
builder.setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.castle))
...

프로젝트의 리소스들은 Drawable로만 가져올 수 있기 때문에 Bitmap으로 변경이 필요합니다. Drawable을 Bitmap으로 변경하거나 Bitmap을 Drawable로 변경하는 방법에 대해서 정리하였습니다.

이 글에서 사용하는 예제는 모두 코틀린으로 작성되었습니다.

Drawable => Bitmap 으로 변경 (1)

getDrawable()는 인자로 전달된 리소스 ID에 대한 이미지를 Drawable로 가져옵니다. Drawable은 BitmapDrawable으로 형변환이 가능하며 BitmapDrawable.bitmap은 bitmap 객체를 리턴합니다.

val drawable = getDrawable(R.drawable.castle)
val bitmapDrawable = drawable as BitmapDrawable
val bitmap = bitmapDrawable.bitmap

Drawable => Bitmap 으로 변경 (2)

다른 방법으로는 BitmapFactory를 사용하는 것입니다. decodeResource()는 인자로 전달된 Drawable ID에 대한 Drawable을 Bitmap 변환하여 리턴해 줍니다. 여기서 resources는 Resources 객체를 말하며 Activity에서 바로 접근할 수 있습니다.

val resources: Resources = this.resources
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.castle)

AdaptiveIconDrawable => Bitmap

Android O에서 Adaptive Icon가 소개되었습니다. 그래서 Android O 이상의 디바이스에서 앱 아이콘은 AdaptiveIconDrawable 객체일 수 있습니다. 이 경우, Bitmap으로 변경하려면 조금 처리가 필요합니다.

Adaptive Icon은 Foreground와 Background 이미지 두개가 합쳐져서 보이는 아이콘인데요. 이 때문에 Bitmap으로 변경하려면 이 두개의 이미지를 합성해야 합니다.

AdaptiveIconDrawable을 Bitmap으로 변경하는 코드는 다음과 같습니다.

val drawable = getDrawable(R.mipmap.ic_launcher)
if (drawable is AdaptiveIconDrawable) {
    val backgroundDrawable = (drawable as AdaptiveIconDrawable).background  // 1
    val foregroundDrawable = (drawable as AdaptiveIconDrawable).foreground  // 2

    val drawables = arrayOfNulls<Drawable>(2)
    drawables[0] = backgroundDrawable
    drawables[1] = foregroundDrawable

    val layerDrawable = LayerDrawable(drawables)  // 3
    val width = layerDrawable.intrinsicWidth
    val height = layerDrawable.intrinsicHeight
    val bitmap = Bitmap.createBitmap(
        width, height, Bitmap.Config.ARGB_8888)  // 4
    val canvas = Canvas(bitmap)  // 5
    layerDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight())
    layerDrawable.draw(canvas)  // 6
}

bitmap // 7

주석 '// 1'처럼 중요한 코드에 번호를 붙였고 아래에 설명하였습니다.

  1. AdaptiveIconDrawable 객체에서 background 이미지만 추출
  2. AdaptiveIconDrawable 객체에서 foreground 이미지만 추출
  3. LayerDrawable로 두개의 객체를 합성. 인자로 전달되는 배열의 가장 큰 Index가 가장 높은 layer에 그려집니다.
  4. 합성된 LayerDrawable의 크기만큼의 Bitmap 객체를 만듭니다.
  5. Canvas 객체를 만듭니다.
  6. Canvas를 통해 LayerDrawable의 이미지를 Bitmap 객체에 draw합니다.
  7. AdaptiveIcon의 모든 레이어가 합성된 Bitmap 객체입니다.

Bitmap => Drawable

간혹 웹에서 Bitmap 이미지를 받아서 디바이스에 보여줄 때 Drawable로 변경해야할 때가 있습니다. 다음 코드처럼 Bitmap 객체를 BitmapDrawable로 wrapping해주면 Drawable 객체로 변경이 됩니다. Android Developer를 보시면 BitmapDrawable은 Drawable을 상속합니다.

val resources: Resources = this.resources
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.castle)
val drawable = BitmapDrawable(resources, bitmap)

정리

Drawable을 Bitmap으로, Bitmap을 Drawable로 변환하는 방법에 대해서 알아보았습니다.

참고