Skip to main content

Handling Images

1. Loading Images from Resources

Property Description Usage
android:src Specifies the image resource to load <ImageView android:src="@drawable/my_image" />
android:scaleType Defines how the image should be resized or moved <ImageView android:scaleType=":centerCrop" />

Example: Loading an Image from Drawable Resource

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/profile_image"
    android:scaleType=&quot:centerCrop"
/>
Note: Image resources should be placed in the res/drawable directory.
Warning: Large images can cause OutOfMemoryError. Use appropriate image sizes and consider using BitmapFactory for decoding.

2. Setting Images from Drawables

Property Values Description
android:src @drawable/image_name Set image from drawable resource
android:background @drawable/image_name Set image as background

Example: Setting Image from Drawable

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/my_image" />

Example: Setting Background from Drawable

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/my_background" />
Note: Drawable resources must be placed in res/drawable/ directory.

3. Loading Images from URIs

Property Description Example
Glide Popular image loading library for Android Glide.with(context).load(imageUri).into(imageView);
Picasso Simple image loading and caching library Picasso.get().load(imageUri).into(imageView);
Coil Modern image loading library using Kotlin Coroutines ImageView.loadImage(imageUri)

Example: Loading Image with Glide

import com.bumptech.glide.Glide

val imageView = findViewById<ImageView>(R.id.imageView)
val imageUri = "https://example.com/image.jpg"

Glide.with(this)
    .load(imageUri)
    .placeholder(R.drawable.placeholder)
    .error(R.drawable.error_image)
    .into(imageView)

Example: Loading Image with Coil

import coil.load

val imageView = findViewById<ImageView>(R.id.imageView)
val imageUri = "https://example.com/image.jpg"

imageView.loadImage(imageUri) {
    placeholder(R.drawable.placeholder)
    error(R.drawable.error_image)
}
Note: When loading images from URIs, always handle potential network errors and provide fallback placeholders for better UX.

4. Decoding Bitmaps

Method Description Parameters Return Type
BitmapFactory.decodeFile() Decodes an image file into a Bitmap path: String Bitmap?
BitmapFactory.decodeResource() Decodes an image resource into a Bitmap res: Resources, id: Int Bitmap?
BitmapFactory.decodeStream() Decodes an image from an InputStream into a Bitmap input: InputStream Bitmap?
BitmapFactory.decodeByteArray() Decodes a byte array into a Bitmap byteArray: ByteArray, offset: Int, length: Int Bitmap?

Example: Decoding a Bitmap from File

val bitmap = BitmapFactory.decodeFile("/sdcard/image.jpg")

Example: Decoding a Bitmap from Resource

val bitmap = BitmapFactory.decodeResource(resources, R.drawable.image)

Example: Decoding a Bitmap from InputStream

val inputStream = FileInputStream("image.png")
val bitmap = BitmapFactory.decodeStream(inputStream)
inputStream.close()

Example: Decoding a Bitmap from Byte Array

val byteArray = File("image.jpg").readBytes()
val bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
Note: For large images, consider using BitmapFactory.Options to downsample and reduce memory usage.

5. Scaling Bitmaps

Property Description Values
BitmapFactory.Options.inSampleSize Scale factor to reduce image size 1 (no scaling) to Integer.MAX_VALUE
BitmapFactory.Options.inJustDecodeBounds Only decode bounds without loading bitmap true or false
BitmapFactory.Options.outWidth Original width of image Int
BitmapFactory.Options.outHeight Original height of image Int

Example: Scaling Bitmaps

val options = BitmapFactory.Options().apply {
    inJustDecodeBounds = true
    BitmapFactory.decodeResource(resources, R.drawable.image, this)
    val imageWidth = outWidth
    val imageHeight = outHeight

    inJustDecodeBounds = false
    inSampleSize = calculateInSampleSize(options, 1024, 1024)
    BitmapFactory.decodeResource(resources, R.drawable.image, this)
}

fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
    val height = options.outHeight
    val width = options.outWidth
    var inSampleSize = 1

    if (height > reqHeight || width > reqWidth) {
        val halfHeight = height / 2
        val halfWidth = width / 2

        while (halfHeight / inSampleSize >= reqHeight &&
               halfWidth / inSampleSize >= reqWidth) {
            inSampleSize *= 2
        }
    }
    return inSampleSize
}
Note: Use inSampleSize to reduce memory usage when loading large images.

6. Creating Bitmaps

Method Description Notes
Bitmap.createBitmap() Creates a new Bitmap with specified dimensions and config Most common way to create bitmaps programmatically
BitmapFactory.decodeResource() Decodes a bitmap from a resource file Useful for loading drawables or image files from resources
BitmapFactory.decodeFile() Decodes a bitmap from a file path Useful for loading images from external storage or cache
BitmapFactory.decodeByteArray() Decodes a bitmap from a byte array Useful for loading images from network or database

Example: Creating a Bitmap from Resource

val bitmap = BitmapFactory.decodeResource(
    context.resources,
    R.drawable.sample_image
)

Example: Creating a Bitmap with Custom Dimensions

val bitmap = Bitmap.createBitmap(
    200,
    200,
    Bitmap.Config.ARGB_8888
)
Note: When creating bitmaps from resources or files, ensure proper memory management to avoid OutOfMemoryError. Consider using BitmapFactory.Options with inSampleSize for large images.

7. Drawing on Canvas

Example: Basic Canvas Drawing

class DrawingView(context: Context) : View(context) {
    private val paint = Paint().apply {
        color = Color.BLUE
        strokeWidth = 5f
        style = Paint.Style.STROKE
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.drawLine(100f, 100f, 300f, 300f, paint)
        canvas.drawCircle(200f, 200f, 50f, paint)
    }
}
Method Description Parameters
drawLine() Draws a line between two points float startX, float startY, float endX, float endY, Paint paint
drawCircle() Draws a circle float cx, float cy, float radius, Paint paint
drawRect() Draws a rectangle float left, float top, float right, float bottom, Paint paint
drawOval() Draws an oval float left, float top, float right, float bottom, Paint paint
drawText() Draws text on canvas String text, float x, float y, Paint paint
Note: Canvas drawing operations are performed in the onDraw() method of a custom View.

Example: Drawing with Gradient

val shader = LinearGradient(
    0f, 0f, 100f, 100f,
    Color.RED, Color.BLUE,
    Shader.TileMode.CLAMP
)
val paint = Paint().apply {
    shader = shader
    style = Paint.Style.FILL
}

canvas.drawRect(0f, 0f, 100f, 100f, paint)

Example: Drawing with Bitmap

val bitmap = BitmapFactory.decodeResource(
    resources, R.drawable.sample_image
)
canvas.drawBitmap(bitmap, 0f, 0f, null)

8. Using Vector Drawables

Vector drawables are XML-based graphics that can be scaled to any size without losing quality. They are ideal for app icons, UI elements, and other graphics that need to be displayed at various resolutions.

Attribute Description Values
android:name Name of the vector drawable resource string
android:width Width of the vector drawable dimension
android:height Height of the vector drawable dimension
android:viewportWidth Viewport width for scaling float
android:viewportHeight Viewport height for scaling float

Example: Defining a Vector Drawable

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:name="vector_drawable"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
  <path
      android:name="path"
      android:pathData="M12,2L2,7L2,17L12,22L22,17L22,7L12,2Z"
      android:fillColor="#FF0000"/>
</vector>

Example: Using Vector Drawable in Layout

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/vector_drawable"/>
Note: Vector drawables are supported from API level 21 (Android 5.0) and above. For older versions, use AppCompat or fallback to raster images.
Warning: Complex vector drawables with many paths may impact performance on older devices.

9. Implementing Image Transformations

Transformation Description Code Example
Circle Crop Crops image into a circular shape
val bitmap = BitmapFactory.decodeResource(context.resources, R.drawable.image)
val circleBitmap = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(circleBitmap)
val paint = Paint()
paint.isAntiAlias = true
canvas.drawCircle(bitmap.width / 2f, bitmap.height / 2f, bitmap.width / 2f, paint)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
canvas.drawBitmap(bitmap, 0f, 0f, paint)
Rounded Corners Rounds the corners of an image
val radius = 20f
val bitmap = BitmapFactory.decodeResource(context.resources, R.drawable.image)
val roundedBitmap = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(roundedBitmap)
val paint = Paint()
paint.isAntiAlias = true
canvas.drawRoundRect(
    0f, 0f, bitmap.width.toFloat(), bitmap.height.toFloat(),
    radius, radius, paint
)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
canvas.drawBitmap(bitmap, 0f, 0f, paint)
Grayscale Converts image to grayscale
val bitmap = BitmapFactory.decodeResource(context.resources, R.drawable.image)
val grayscaleBitmap = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(grayscaleBitmap)
val paint = Paint()
val colorMatrix = ColorMatrix().apply {
    setScale(0.3f, 0.59f, 0.11f, 0f)
}
paint.colorFilter = ColorMatrixColorFilter(colorMatrix)
canvas.drawBitmap(bitmap, 0f, 0f, paint)
Blur Effect Applies a blur effect to the image
val bitmap = BitmapFactory.decodeResource(context.resources, R.drawable.image)
val blurredBitmap = FastBlur.blur(bitmap, 25f, true)
Note: For performance, consider using RenderScript or GPUImage for complex image transformations.

10. Handling Image Memory

Property Description Notes
Bitmap Represents an image in memory Can cause OutOfMemoryError if not managed properly
BitmapFactory Used to decode image files into Bitmaps Options like inSampleSize help reduce memory usage
inSampleSize Decoding option to scale down image Reduces memory footprint by factor of inSampleSize²
LruCache Least Recently Used cache for Bitmaps Automatically evicts least used entries when memory limit is reached

Example: Loading Scaled Bitmap

fun decodeSampledBitmapFromResource(
    res: Resources,
    resId: Int,
    reqWidth: Int,
    reqHeight: Int
): Bitmap {
    // First decode with inJustDecodeBounds=true to check dimensions
    val options = BitmapFactory.Options().apply {
        inJustDecodeBounds = true
    }
    BitmapFactory.decodeResource(res, resId, options)

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false
    return BitmapFactory.decodeResource(res, resId, options)
}

fun calculateInSampleSize(
    options: BitmapFactory.Options,
    reqWidth: Int,
    reqHeight: Int
): Int {
    val height = options.outHeight
    val width = options.outWidth
    var inSampleSize = 1

    if (height > reqHeight || width > reqWidth) {
        val halfHeight = height / 2
        val halfWidth = width / 2

        while ((halfHeight / inSampleSize) >= reqHeight &&
               (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2
        }
    }
    return inSampleSize
}

Example: Using LruCache for Bitmaps

class BitmapLruCache : LruCache<String, Bitmap>(maxSize()) {
    companion object {
        private fun maxSize(): Int = (Runtime.getRuntime().maxMemory() / 8).toInt()
    }

    override fun sizeOf(key: String, bitmap: Bitmap): Int = bitmap.byteCount
}

val cache = BitmapLruCache()

fun getBitmapFromCache(key: String): Bitmap? = cache.get(key)

fun addBitmapToCache(key: String, bitmap: Bitmap) {
    if (getBitmapFromCache(key) == null) {
        cache.put(key, bitmap)
    }
}
Note: Always consider using inSampleSize when loading large images to prevent OutOfMemoryError. Also, use Bitmap.recycle() when done with a bitmap to free up memory immediately.

11. Using Coil for Image Loading

Property Description Example
load() Starts loading an image from a URL or resource Coil.load("https://example.com/image.jpg")
placeholder() Displays a placeholder while image loads Coil.load("image.jpg").placeholder(R.drawable.placeholder)
error() Displays an error image if loading fails Coil.load("image.jpg").error(R.drawable.error)
size() Sets the target size for image loading Coil.load("image.jpg").size(200, 200)

Example: Basic Image Loading with Coil

import coil.compose.AsyncImage
import androidx.compose.runtime.Composable

@Composable
fun ImageLoader() {
    AsyncImage(
        model = "https://example.com/image.jpg",
        contentDescription = "Example Image"
    )
}

Example: Image Loading with Placeholder and Error Handling

import coil.compose.AsyncImage
import androidx.compose.runtime.Composable

@Composable
fun ImageWithFallback() {
    AsyncImage(
        model = "https://example.com/image.jpg",
        contentDescription = "Example Image",
        placeholder = rememberAsyncImagePainter("https://example.com/placeholder.jpg"),
        error = rememberAsyncImagePainter("https://example.com/error.png")
    )
}
Note: Coil uses a caching mechanism to avoid re-fetching images unnecessarily.

Was this article helpful?